# Always print this out before your assignment
sessionInfo()
R version 4.1.1 (2021-08-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 18362)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252 LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] corrplot_0.92              ggridges_0.5.3             glmnetUtils_1.1.8          glmnet_4.1-2              
 [5] Matrix_1.3-4               scales_1.1.1               tidyquant_1.0.3            quantmod_0.4.18           
 [9] TTR_0.24.2                 PerformanceAnalytics_2.0.4 xts_0.12.1                 zoo_1.8-9                 
[13] plotly_4.10.0              viridis_0.6.2              viridisLite_0.4.0          pastecs_1.3.21            
[17] kableExtra_1.3.4           lubridate_1.8.0            rsample_0.1.0              ggthemes_4.2.4            
[21] ggrepel_0.9.1              here_1.0.1                 fs_1.5.0                   forcats_0.5.1             
[25] stringr_1.4.0              dplyr_1.0.7                purrr_0.3.4                readr_2.0.2               
[29] tidyr_1.1.4                tibble_3.1.5               ggplot2_3.3.5              tidyverse_1.3.1           
[33] knitr_1.36                

loaded via a namespace (and not attached):
 [1] colorspace_2.0-2  ellipsis_0.3.2    rprojroot_2.0.2   rstudioapi_0.13   listenv_0.8.0     furrr_0.2.3      
 [7] farver_2.1.0      bit64_4.0.5       fansi_0.5.0       xml2_1.3.2        splines_4.1.1     codetools_0.2-18 
[13] jsonlite_1.7.2    broom_0.7.9       dbplyr_2.1.1      compiler_4.1.1    httr_1.4.2        backports_1.3.0  
[19] assertthat_0.2.1  fastmap_1.1.0     lazyeval_0.2.2    cli_3.0.1         htmltools_0.5.2   tools_4.1.1      
[25] gtable_0.3.0      glue_1.4.2        Rcpp_1.0.7        cellranger_1.1.0  jquerylib_0.1.4   vctrs_0.3.8      
[31] svglite_2.0.0     iterators_1.0.13  crosstalk_1.2.0   xfun_0.27         globals_0.14.0    rvest_1.0.2      
[37] lifecycle_1.0.1   pacman_0.5.1      future_1.22.1     vroom_1.5.5       hms_1.1.1         parallel_4.1.1   
[43] yaml_2.2.1        curl_4.3.2        gridExtra_2.3     sass_0.4.0        stringi_1.7.5     highr_0.9        
[49] foreach_1.5.1     boot_1.3-28       shape_1.4.6       rlang_0.4.11      pkgconfig_2.0.3   systemfonts_1.0.3
[55] evaluate_0.14     lattice_0.20-44   htmlwidgets_1.5.4 labeling_0.4.2    bit_4.0.4         tidyselect_1.1.1 
[61] parallelly_1.28.1 plyr_1.8.6        magrittr_2.0.1    R6_2.5.1          generics_0.1.0    DBI_1.1.1        
[67] pillar_1.6.3      haven_2.4.3       withr_2.4.2       survival_3.2-11   modelr_0.1.8      crayon_1.4.1     
[73] Quandl_2.11.0     utf8_1.2.2        tzdb_0.1.2        rmarkdown_2.11    grid_4.1.1        readxl_1.3.1     
[79] data.table_1.14.2 reprex_2.0.1      digest_0.6.28     webshot_0.5.2     munsell_0.5.0     bslib_0.3.1      
[85] quadprog_1.5-8   
getwd()
[1] "C:/Users/cabrooke/Documents/GitHub/BROCODE_Final_Project"

# load all your libraries in this chunk 
library('tidyverse')
library("fs")
library('here')
library('dplyr')
library('tidyverse')
library('ggplot2')
library('ggrepel')
library('ggthemes')
library('forcats')
library('rsample')
library('lubridate')
library('ggthemes')
library('kableExtra')
library('pastecs')
library('viridis')
library('plotly')
library('tidyquant')
library('scales')
library("gdata")
gdata: Unable to locate valid perl interpreter
gdata: 
gdata: read.xls() will be unable to read Excel XLS and XLSX files unless the 'perl=' argument is used
gdata: to specify the location of a valid perl intrpreter.
gdata: 
gdata: (To avoid display of this message in the future, please ensure perl is installed and available
gdata: on the executable search path.)
gdata: Unable to load perl libaries needed by read.xls()
gdata: to support 'XLX' (Excel 97-2004) files.

gdata: Unable to load perl libaries needed by read.xls()
gdata: to support 'XLSX' (Excel 2007+) files.

gdata: Run the function 'installXLSXsupport()'
gdata: to automatically download and install the perl
gdata: libaries needed to support Excel XLS and XLSX formats.

Attaching package: ‘gdata’

The following objects are masked from ‘package:xts’:

    first, last

The following objects are masked from ‘package:pastecs’:

    first, last

The following objects are masked from ‘package:dplyr’:

    combine, first, last

The following object is masked from ‘package:purrr’:

    keep

The following object is masked from ‘package:stats’:

    nobs

The following object is masked from ‘package:utils’:

    object.size

The following object is masked from ‘package:base’:

    startsWith
# note, do not run install.packages() inside a code chunk. install them in the console outside of a code chunk. 

Part 1 - Final Project Cleaning and Summary Statistics

1a) Loading data


#Reading the data in and doing minor initial cleaning in the function call
#Reproducible data analysis should avoid all automatic string to factor conversions.
#strip.white removes white space 
#na.strings is a substitution so all that have "" will = na
data <- read.csv(here::here("final_project", "donor_data.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

1b) Fixing the wonky DOB & Data cleanup


#(Birthdate and Age, ID as a number)adding DOB (Age/Spouse Age) in years columns and adding two fields for assignment and number of children and number of degrees
dataclean <- data %>%
  mutate(Birthdate = ifelse(Birthdate == "0001-01-01", NA, Birthdate)) %>%
  mutate(Birthdate = mdy(Birthdate)) %>%
  mutate(Age = as.numeric(floor(interval(start= Birthdate, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(Spouse.Birthdate = ifelse(Spouse.Birthdate == "0001-01-01", NA, Spouse.Birthdate)) %>%
  mutate(Spouse.Birthdate = mdy(Spouse.Birthdate)) %>%
  mutate(Spouse.Age = as.numeric(floor(interval(start= Spouse.Birthdate,
                                                end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(ID = as.numeric(ID)) %>% 
  mutate(Assignment_flag = ifelse(is.na(Assignment.Number), 0,1)) %>% 
  mutate( No_of_Children = ifelse(is.na(Child.1.ID),0,
                            ifelse(is.na(Child.2.ID),1,2)))%>%
 mutate(ID = as.numeric(ID)) %>% 
    mutate( nmb_degree = ifelse(is.na(Degree.Type.1),0,
                            ifelse(is.na(Degree.Type.2),1,2)))
#conferral dates
dataclean <- dataclean %>%
  
  mutate(Conferral.Date.1 = ifelse(Conferral.Date.1 == "0001-01-01", NA, Conferral.Date.1)) %>%
  mutate(Conferral.Date.1 = mdy(Conferral.Date.1)) %>%
  mutate(Conferral.Date.1.Age = as.numeric(floor(interval(start= Conferral.Date.1, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Conferral.Date.2 = ifelse(Conferral.Date.2 == "0001-01-01", NA, Conferral.Date.2)) %>%
  mutate(Conferral.Date.2 = mdy(Conferral.Date.2)) %>%
  mutate(Conferral.Date.2.Age = as.numeric(floor(interval(start= Conferral.Date.2, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Last.Contact.By.Anyone = ifelse(Last.Contact.By.Anyone == "0001-01-01", NA, Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.By.Anyone = mdy(Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.Age = as.numeric(floor(interval(start= Last.Contact.By.Anyone, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
 mutate(HH.First.Gift.Date = ifelse(HH.First.Gift.Date == "0001-01-01", NA, HH.First.Gift.Date)) %>%
  mutate(HH.First.Gift.Date = mdy(HH.First.Gift.Date)) %>%
mutate(HH.First.Gift.Age = as.numeric(floor(interval(start= HH.First.Gift.Date, end=Sys.Date())/duration(n=1, unit="years"))))

#major gift 
dataclean <- 
  dataclean %>% 
  mutate(major_gifter = ifelse(Lifetime.Giving > 50000, 1,0) %>% factor(., levels = c("0","1")))


#splitting up the age into ranges and creating category for easy visualization 
dataclean <- dataclean %>%
  mutate(age_range = 
    ifelse(Age %in% 10:19, "10 < 20 years old",
    ifelse(Age %in% 20:29, "20 < 30 years old", 
    ifelse(Age %in% 30:39, "30 < 40 years old",
    ifelse(Age %in% 40:49, "40 < 50 years old",
    ifelse(Age %in% 50:59, "50 < 60 years old",
    ifelse(Age %in% 60:69, "60 < 70 years old",
    ifelse(Age %in% 70:79, "70 < 80 years old",
    ifelse(Age %in% 80:89, "80 < 90 years old",
    ifelse(Age %in% 90:120, "90+ years old",
    NA))))))))))


#seeing what we have
table(dataclean$age_range)

10 < 20 years old 20 < 30 years old 30 < 40 years old 40 < 50 years old 50 < 60 years old 60 < 70 years old 
             3985             24558             21037             16851             20755             18257 
70 < 80 years old 80 < 90 years old     90+ years old 
            12246              5984              6633 
#50-60 is the most common age range 

#creating a region column using the county data and the OMB MSA (Metropolitan Statistical Area) definitions

dataclean <- dataclean %>%
  mutate(region = 
    ifelse(County == "San Luis Obispo" & State == "CA", "So Cal",
    ifelse(County == "Kern" & State == "CA", "So Cal",
    ifelse(County == "San Bernardino" & State == "CA", "So Cal",
    ifelse(County == "Santa Barbara" & State == "CA", "So Cal",
    ifelse(County == "Ventura" & State == "CA", "So Cal",
    ifelse(County == "Los Angeles" & State == "CA", "So Cal",
    ifelse(County == "Orange" & State == "CA", "So Cal",
    ifelse(County == "Riverside" & State == "CA", "So Cal",
    ifelse(County == "San Diego" & State == "CA", "So Cal",
    ifelse(County == "Imperial" & State == "CA", "So Cal",
    ifelse(County == "King" & State == "WA", "Seattle",
    ifelse(County == "Snohomish" & State == "WA", "Seattle",
    ifelse(County == "Pierce" & State == "WA", "Seattle",
    ifelse(County == "Clackamas" & State == "OR", "Portland",
    ifelse(County == "Columbia" & State == "OR", "Portland",
    ifelse(County == "Multnomah" & State == "OR", "Portland",
    ifelse(County == "Washington" & State == "OR", "Portland",
    ifelse(County == "Yamhill" & State == "OR", "Portland",
    ifelse(County == "Clark" & State == "WA", "Portland",
    ifelse(County == "Skamania" & State == "WA", "Portland",
    ifelse(County == "Denver" & State == "CO", "Denver",
    ifelse(County == "Arapahoe" & State == "CO", "Denver",
    ifelse(County == "Jefferson" & State == "CO", "Denver",
    ifelse(County == "Adams" & State == "CO", "Denver",
    ifelse(County == "Douglas" & State == "CO", "Denver",
    ifelse(County == "Broomfield" & State == "CO", "Denver",    
    ifelse(County == "Elbert" & State == "CO", "Denver",
    ifelse(County == "Park" & State == "CO", "Denver",
    ifelse(County == "Clear Creek" & State == "CO", "Denver",
    ifelse(County == "Alameda" & State == "CA", "Bay Area",
    ifelse(County == "Contra Costa" & State == "CA", "Bay Area",
    ifelse(County == "Marin" & State == "CA", "Bay Area",
    ifelse(County == "Monterey" & State == "CA", "Bay Area",
    ifelse(County == "Napa" & State == "CA", "Bay Area",
    ifelse(County == "San Benito" & State == "CA", "Bay Area",
    ifelse(County == "San Francisco" & State == "CA", "Bay Area",
    ifelse(County == "San Mateo" & State == "CA", "Bay Area",
    ifelse(County == "Santa Clara" & State == "CA", "Bay Area",
    ifelse(County == "Santa Cruz" & State == "CA", "Bay Area",
    ifelse(County == "Solano" & State == "CA", "Bay Area",
    ifelse(County == "Sonoma" & State == "CA", "Bay Area",
           NA))))))))))))))))))))))))))))))))))))))))))

dataclean <- dataclean %>%
  mutate(region = 
    ifelse(County == "Kings" & State == "NY", "New York",
    ifelse(County == "Queens" & State == "NY", "New York",
    ifelse(County == "New York" & State == "NY", "New York",
    ifelse(County == "Bronx" & State == "NY", "New York",
    ifelse(County == "Richmond" & State == "NY", "New York",
    ifelse(County == "Westchester" & State == "NY", "New York",
    ifelse(County == "Bergen" & State == "NY", "New York",
    ifelse(County == "Hudson" & State == "NY", "New York",
    ifelse(County == "Passaic" & State == "NY", "New York",
    ifelse(County == "Putnam" & State == "NY", "New York",
    ifelse(County == "Rockland" & State == "NY", "New York",
    ifelse(County == "Suffolk" & State == "NY", "New York",
    ifelse(County == "Nassau" & State == "NY", "New York",
    ifelse(County == "Middlesex" & State == "NJ", "New York",
    ifelse(County == "Monmouth" & State == "NJ", "New York",
    ifelse(County == "Ocean" & State == "NJ", "New York",
    ifelse(County == "Somerset" & State == "NJ", "New York",
    ifelse(County == "Essex" & State == "NJ", "New York",
    ifelse(County == "Union" & State == "NJ", "New York",
    ifelse(County == "Morris" & State == "NJ", "New York",
    ifelse(County == "Sussex" & State == "NJ", "New York",
    ifelse(County == "Hunterdon" & State == "NJ", "New York",
    ifelse(County == "Pike" & State == "NJ", "New York",
    region))))))))))))))))))))))))


# code nor cal region as all others in CA not already defined

dataclean <- dataclean %>%
  mutate(region = 
    ifelse(State == "CA" & is.na(region) == TRUE, "Nor Cal", region))


#Removing Columns that provide no benefit 

dataclean <- subset(dataclean,select = -c(Assignment.Number
                                                    ,Assignment.has.Historical.Mngr
                                                    ,Suffix
                                                    ,Assignment.Date
                                                    ,Assignment.Manager
                                                    ,Assignment.Role
                                                    ,Assignment.Title
                                                    ,Assignment.Status
                                                    ,Strategy
                                                    ,Progress.Level
                                                    ,Assignment.Group
                                                    ,Assignment.Category
                                                    ,Funding.Method
                                                        ,Expected.Book.Date
                                                        ,Qualification.Amount
                                                        ,Expected.Book.Amount
                                                        ,Expected.Book.Date
                                                        ,Hard.Gift.Total
                                                        ,Soft.Credit.Total
                                                        ,Total.Assignment.Gifts
                                                        ,No.of.Pledges
                                                        ,Proposal..
                                                        ,Proposal.Notes
                                                        ,HH.Life.Spouse.Credit
                                                        ,Last.Contact.By.Manager
                                                        ,X..of.Contacts.By.Manager
                                                        ,DonorSearch.Range
                                                        ,iWave.Range
                                                        ,WealthEngine.Range
                                                        ,Philanthropic.Commitments
                                                        ))
#cleaning up zip codes removing -4 after 
dataclean$Zip <- gsub(dataclean$Zip, pattern="-.*", replacement = "")

#adding zip code data and column 
zip <- read.csv(here::here("final_project", "Salary_Zipcode.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")


#adding zip salary column
dataclean <-dataclean %>%
    mutate(zipcode_slry = VLOOKUP(Zip, zip, NAME, S1902_C03_002E))

#slry range 
dataclean <- dataclean %>%
  mutate(zipslry_range = 
    ifelse(zipcode_slry %in% 10000:89999, "90K-99K",
    ifelse(zipcode_slry %in% 90000:99999, "90K-99K",
    ifelse(zipcode_slry %in% 100000:149999, "100K-149K", 
    ifelse(zipcode_slry %in% 150000:199999, "150K-199K",
    ifelse(zipcode_slry %in% 200000:249999, "200K-249K",
    ifelse(zipcode_slry %in% 250000:299999, "250K-299K",
    ifelse(zipcode_slry %in% 300000:349999, "300K-349K",
    ifelse(zipcode_slry %in% 350000:399999, "350K-399K",
    ifelse(zipcode_slry %in% 400000:499999, "400K-499K",
    ifelse(zipcode_slry %in% 500000:999999, "500K-999K",
    NA)))))))))))

sum(is.na(dataclean$zipcode_slry))
[1] 62347
#adding scholarship data (y/n)
schlr <- read.csv(here::here("final_project", "scholarship.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

#adding scholarship column
dataclean <-dataclean %>%
    mutate(scholarship = VLOOKUP(ID, schlr, ID, SCHOLARSHIP)) 

#replacing NA with 0 
 dataclean$scholarship <- replace_na(dataclean$scholarship,'0')
 
#replacing Y with 1 
dataclean$scholarship<-ifelse(dataclean$scholarship=="Y",1,0)

#checking how many are N
table(dataclean$scholarship)

     0      1 
295264  27962 
#checking and deleting scholarship column 
class(dataclean$schlr_fct)
[1] "NULL"
dataclean = subset(dataclean, select = -c(scholarship))
  
#checking for duplicates N >1 indicates a records values are in the file twice 
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))

#removing duplicated records

dataclean <- unique(dataclean)

#n = 1 no ID with multiple records cleaned of dupes
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))
NA

1d Creating many many factor variables


dataclean <- 
  dataclean %>% 
  #SEX
  mutate(sex_fct = 
           fct_explicit_na(Sex),
sex_simple = 
    fct_lump_n(Sex, n = 4),
#MARRIED
married_fct = 
           fct_explicit_na(Married),
  #DONOR SEGMENT
  donorseg_fct = 
           fct_explicit_na(Donor.Segment),
         donorseg_simple = 
           fct_lump_n(Donor.Segment, n = 4),
  #CONTACT RULE
         contact_fct = 
           fct_explicit_na(Contact.Rules),
         contact_simple = 
           fct_lump_n(Contact.Rules, n = 4),
  #SPOUSE MAIL
         spomail_fct = 
           fct_explicit_na(Spouse.Mail.Rules),
         spomail_simple = 
           fct_lump_n(Spouse.Mail.Rules, n = 4),
  #JOB TITLE
         jobtitle_fct = 
           fct_explicit_na(Job.Title),
         jobtitle_simple = 
           fct_lump_n(Job.Title, n = 5),
  #DEGREE TYPE 1
         deg1_fct = 
           fct_explicit_na(Degree.Type.1),
         deg1_simple = 
           fct_lump_n(Degree.Type.1, n = 5),
  #DEGREE TYPE 2
         deg2_fct = 
           fct_explicit_na(Degree.Type.2),
         deg2_simple = 
           fct_lump_n(Degree.Type.2, n = 5),
  #MAJOR 1
         maj1_fct = 
           fct_explicit_na(Major.1),
         maj1_simple = 
           fct_lump_n(Major.1, n = 5),
  #MAJOR 2
         maj2_fct = 
           fct_explicit_na(Major.2),
         maj2_simple = 
           fct_lump_n(Major.2, n = 5),
  #MINOR 1
         min1_fct = 
           fct_explicit_na(Minor.1),
         min1_simple = 
           fct_lump_n(Minor.1, n = 5),
  #MINOR 2
         min2_fct = 
           fct_explicit_na(Minor.2),
         min2_simple = 
           fct_lump_n(Minor.2, n = 5),
  #SCHOOL 1
         school1_fct = 
           fct_explicit_na(School.1),
         school1_simple = 
           fct_lump_n(School.1, n = 5),
  #SCHOOL 2
         school2_fct = 
           fct_explicit_na(School.2),
         school2_simple = 
           fct_lump_n(School.2, n = 5),
  #INSTITUTION TYPE
         insttype_fct = 
           fct_explicit_na(Institution.Type),
         insttype_simple = 
           fct_lump_n(Institution.Type, n = 5),
  #EXTRACURRICULAR
         extra_fct = 
           fct_explicit_na(Extracurricular),
         extra_simple = 
           fct_lump_n(Extracurricular, n = 5),
  #HH FIRST GIFT FUND
         hhfirstgift_fct = 
           fct_explicit_na(HH.First.Gift.Fund),
         hhfirstgift_simple = 
           fct_lump_n(HH.First.Gift.Fund, n = 5),
#CHILD 1 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.1.Enroll.Status),
         ch1_enroll_simple = 
           fct_lump_n(Child.1.Enroll.Status, n = 4),
#CHILD 1 MAJOR
         ch1_maj_fct = 
           fct_explicit_na(Child.1.Major),
         ch1_maj_simple = 
           fct_lump_n(Child.1.Major, n = 4),
#CHILD 1 MINOR
         ch1_min_fct = 
           fct_explicit_na(Child.1.Minor),
         ch1_min_simple = 
           fct_lump_n(Child.1.Minor, n = 4),
#CHILD 1 SCHOOL
         ch1_school_fct = 
           fct_explicit_na(Child.1.School),
         ch1_school_simple = 
           fct_lump_n(Child.1.School, n = 4),
#CHILD 1 FEEDER
         ch1_feeder_fct = 
           fct_explicit_na(Child.1.Feeder.School),
         ch1_feeder_simple = 
           fct_lump_n(Child.1.Feeder.School, n = 4),
#CHILD 2 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.2.Enroll.Status),
         ch2_enroll_simple = 
           fct_lump_n(Child.2.Enroll.Status, n = 4),
#CHILD 2 MAJOR
         ch2_maj_fct = 
           fct_explicit_na(Child.2.Major),
         ch2_maj_simple = 
           fct_lump_n(Child.2.Major, n = 4),
#CHILD 2 MINOR
         ch2_min_fct = 
           fct_explicit_na(Child.2.Minor),
         ch2_min_simple = 
           fct_lump_n(Child.2.Minor, n = 4),
#CHILD 2 SCHOOL
         ch2_school_fct = 
           fct_explicit_na(Child.2.School),
         ch2_school_simple = 
           fct_lump_n(Child.2.School, n = 4),
#CHILD 2 FEEDER
         ch2_feeder_fct = 
           fct_explicit_na(Child.2.Feeder.School),
         ch2_feeder_simple = 
           fct_lump_n(Child.2.Feeder.School, n = 4),
    )



#checking to see if its a factor
#class(dataclean$sex_fct)
#class(dataclean$donorseg_fct)
#class(dataclean$contact_fct)
#class(dataclean$spomail_fct)

#checking levels
#levels(dataclean$sex_simple)
#levels(dataclean$donorseg_simple)
#levels(dataclean$contact_simple)
#levels(dataclean$spomail_simple)
#levels(dataclean$hhfirstgift_simple)

#creating a table against Sex column 
#table(dataclean$sex_fct, dataclean$sex_simple)

Region Analysis

Region Count Mean HH Lifetime Giving
So Cal 145139 $5,090.84
NA 130306 $2,040.98
Bay Area 20641 $755.92
Nor Cal 10707 $3,823.63
Seattle 5425 $922.08
New York 4959 $1,978.49
Portland 2976 $1,098.24
Denver 2847 $257.29

DonorSegment Analysis

Donor Segment Count Mean HH Lifetime Giving
NA 231974 $0.00
Lost Donor 69718 $4,954.47
Lapsed Donor 11193 $10,069.79
Current Donor 5603 $90,638.32
Lapsing Donor 3862 $16,590.15
At-Risk Donor 650 $77,143.93

First gift size

aq <- quantile(dataclean$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

aq <- as.data.frame(aq)

aq$aq <- dollar(aq$aq)

aq %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Quantile
25% $0.00
50% $0.00
75% $0.00
90% $40.00
99% $1,910.06
NA
NA

Consecutive giving

#consecutive years of giving 
dataclean %>%
  filter(Max.Consec.Fiscal.Years > 0) %>%
  ggplot(aes(Max.Consec.Fiscal.Years)) + geom_histogram(fill = "#002845", bins = 20) + 
  theme_economist_white() +
  ggtitle("Consecutive Years of Giving Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,2)) +
  scale_y_continuous(breaks = seq(0,10000000,5000)) 

NA
NA
NA

Lifetime giving based on number of children

dataclean %>%
  filter(HH.Lifetime.Giving <= 10000) %>%
  filter(HH.Lifetime.Giving > 0) %>%
  mutate(`No_of_Children` = as.factor(`No_of_Children`)) %>%
  ggplot(aes(HH.Lifetime.Giving, fill = `No_of_Children`)) + geom_histogram(bins = 30) + theme_economist_white() +
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,100000,1000)) +
  scale_y_continuous(breaks = seq(0,100000000,5000)) +
  ggtitle("Giving distribution and number of children")+ 
  scale_fill_manual(values=c("#002845", "#00cfcc", "#ff9973"))

NA
NA
NA

Mean, Median, and Count of Giving in Age Ranges


age_range_giving <- dataclean %>%
  group_by(age_range) %>%
  summarise(avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
            med_giving = median(HH.Lifetime.Giving, na.rm = TRUE),
            amount_of_people_in_age_range = n())


glimpse(age_range_giving)
Rows: 10
Columns: 4
$ age_range                     <chr> "10 < 20 years old", "20 < 30 years old", "30 < 40 years old", "40 < 50 ye~
$ avg_giving                    <dbl> 0.4455282, 28.2744487, 391.1692413, 804.5846468, 2779.1345908, 5401.706827~
$ med_giving                    <dbl> 0, 0, 0, 0, 0, 0, 0, 10, 15, 0
$ amount_of_people_in_age_range <int> 3985, 24551, 21024, 16831, 20737, 18226, 12195, 5954, 6626, 192871

Part 2

2a) Plotting average giving by age range


age_range_giving <-
  age_range_giving %>%
  mutate(age_range = factor(age_range))

ggplot(age_range_giving, aes(age_range, avg_giving)) +
  geom_bar(stat = "identity")+
  theme(axis.text.x = element_text(angle=45,
                                        hjust=1))

NA
NA

2b) Count of donors based on age range (another way to look at it)


ggplot(dataclean, 
       aes(age_range)) + 
       geom_bar() + 
       theme(axis.text.x = element_text(angle=45,
                                        hjust=1)) + 
  labs(title = "Count of Age Ranges", x = "", y = "")

NA
NA

2c) Boxplot of the Age Ranges Against the Lifetime Giving Amounts with a log scale applied - the reason we applied log scale is to resolve issues with visualizations that skew towards large values in our dataset.


ggplot(dataclean, aes(age_range,HH.Lifetime.Giving,fill = age_range)) + 
  geom_boxplot(
  outlier.colour = "red") + 
  scale_y_log10() +
  theme(axis.text.x=element_text(angle=45,hjust=1))

NA
NA

2d) Splitting by age and gender



#creating boxplots 
dataclean %>% 
  filter(Age < 100) %>% #removing the weird outliers that are over 100 
  filter(Sex %in% c("M", "F")) %>%
  ggplot(aes(Sex, Age)) + 
  geom_boxplot() + 
  theme_economist() + 
  ggtitle("Ages of Donors Based on Gender") + 
  xlab(NULL) + ylab(NULL)

NA
NA

Giving by gender


#remove NAs U X 
q <- ggplot(dataclean)
q + stat_summary_bin(
  aes(y = HH.Lifetime.Giving, x = sex_simple), 
  fun.y = "mean", geom = "bar")


summary(dataclean$sex_simple)
     F      M      U      X   NA's 
120781 108190   3683      7  90339 

Mean age by gender

#breakdown of sexs 
tally(group_by(dataclean, Sex))

summarize(group_by(dataclean, Sex), 
          avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
          avg_age = mean(Age, na.rm = TRUE),
          med_age = median(Age, na.rm = TRUE))

#grouping by sex and age range for slides 
tally(group_by(dataclean, Sex, age_range))
NA
NA
NA

2e) Distribution of people in the states that they live.


  dataclean %>%
  mutate(State = ifelse(State == " ", "NA", State)) %>%
  filter(State != "NA") %>%
  group_by(State) %>%
  summarise(Count = length(State)) %>%
  filter(Count > 800) %>%
  arrange(-Count) %>%
  kable(col.names = c("Donor's State", "Count")) %>%
  kable_styling(bootstrap_options = c("condensed"),
                full_width = F)
Donor's State Count
CA 176487
WA 7957
TX 7266
NY 5659
CO 5073
AZ 4925
OR 4612
FL 4111
IL 3681
HI 3394
PA 2904
OH 2754
NV 2715
MI 2523
MA 2473
NJ 2311
VA 2158
NC 2085
GA 2044
MO 1889
MN 1732
MD 1488
TN 1443
IN 1417
CT 1380
WI 1330
UT 1173
OK 1151
AL 1120
LA 1110
ID 1096
SC 1076
KY 1032
KS 1027
NM 981
IA 880
NA
NA
NA
NA
NA
NA

2f) Looking at all donors first gift amount. 75% made a first gift of <100.


 no_non_donors <- dataclean %>%
  filter(Lifetime.Giving != 0)
  
nd <- quantile(no_non_donors$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

nd <- as.data.frame(nd)

nd %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Quantile
25% 3.8
50% 25.0
75% 100.0
90% 500.0
99% 15000.0
NA
NA
NA
NA

Modeling for you

Split data

#converting married Y and N to 1 and 0 
dataclean <- dataclean %>%
      mutate(Married_simple = ifelse(Married == "N",0,1))


dataclean <- dataclean %>%
  mutate(hh.lifetime.giving_fct = as.factor(HH.Lifetime.Giving))


library("rsample")

data_split <- initial_split(dataclean, prop = 0.75)

data_train <- training(data_split)
data_test <- testing(data_split)
p <- dataclean %>%
  ggplot(aes(Age)) + geom_histogram(bins=30, fill = "blue") + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(5,100,by = 20)) +
  scale_y_continuous(breaks = seq(20,100,by = 20)) + xlim(c(20,100))
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
ggplotly(p)
  
p


ggplot(data = dataclean, aes(x = Age)) + geom_histogram(fill ="blue")+ xlim(c(20,100))
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

NA
NA
NA

Another Histogram


dataclean %>%
  filter(Age >= 10) %>%
  filter(Age <= 90) %>%
  ggplot(aes(Age)) + geom_histogram(fill = "#002845", bins = 20) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,10000000,2000)) 

Age distribution by gender

#Age Gender filtered out below 15 and above 90 - also removed U X the weird values 
dataclean %>%
  filter(Age >= 15) %>%
  filter(Age <= 90) %>%
  mutate(Sex = as.factor(Sex)) %>%
  filter(Sex != "U") %>%
  filter(Sex != "X") %>%
  ggplot(aes(Age, fill = Sex)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Age Distribution by Gender") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,10)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Donor age distribution by marital status

#Age Marital Status
dataclean %>%
  filter(Age >= 20) %>%
  filter(Age <= 85) %>%
  ggplot(aes(Age, fill = Married)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution by Marital Status") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Linear Model

summary(mod3lm)

Call:
lm(formula = Lifetime.Giving ~ region, data = data_train)

Residuals:
     Min       1Q   Median       3Q      Max 
   -4053    -4053    -4053    -3164 18110072 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)       589.7      966.0   0.610 0.541571    
regionDenver     -438.6     2759.7  -0.159 0.873736    
regionNew York   1773.6     2193.3   0.809 0.418707    
regionNor Cal    2573.9     1650.9   1.559 0.118990    
regionPortland    251.4     2715.6   0.093 0.926243    
regionSeattle    -217.6     2125.1  -0.102 0.918430    
regionSo Cal     3462.9     1032.4   3.354 0.000796 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 120100 on 144441 degrees of freedom
  (97802 observations deleted due to missingness)
Multiple R-squared:  0.0001186, Adjusted R-squared:  7.706e-05 
F-statistic: 2.855 on 6 and 144441 DF,  p-value: 0.008814

MORE MODELS

Big logistic model


# Set family to binomial to set logistic function
# Run the model on the training set

donor_logit1 <-
  glm(hh.lifetime.giving_fct ~ Married_simple,
      family = "binomial",
      data = data_train)

summary(donor_logit1)

Call:
glm(formula = hh.lifetime.giving_fct ~ Married_simple, family = "binomial", 
    data = data_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.1122  -0.6872  -0.6872   1.2440   1.7659  

Coefficients:
                Estimate Std. Error z value            Pr(>|z|)    
(Intercept)    -1.323203   0.005906  -224.1 <0.0000000000000002 ***
Married_simple  1.167887   0.009628   121.3 <0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 288365  on 242249  degrees of freedom
Residual deviance: 273662  on 242248  degrees of freedom
AIC: 273666

Number of Fisher Scoring iterations: 4
donor_logit2 <-
  glm(hh.lifetime.giving_fct ~ No_of_Children,
      family = "binomial",
      data = data_train)

summary(donor_logit2)

Call:
glm(formula = hh.lifetime.giving_fct ~ No_of_Children, family = "binomial", 
    data = data_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-0.9083  -0.8000  -0.8000   1.5411   1.6094  

Coefficients:
                Estimate Std. Error z value            Pr(>|z|)    
(Intercept)    -0.975131   0.005212 -187.10 <0.0000000000000002 ***
No_of_Children  0.151469   0.009049   16.74 <0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 288365  on 242249  degrees of freedom
Residual deviance: 288089  on 242248  degrees of freedom
AIC: 288093

Number of Fisher Scoring iterations: 4
#summary(data_train$major_gifter)

donor_logit3 <-
  glm(major_gifter ~ Married_simple + No_of_Children + donorseg_simple + Assignment_flag + Total.Giving.Years,
      family = "binomial",
      data = data_train)

summary(donor_logit3)

Call:
glm(formula = major_gifter ~ Married_simple + No_of_Children + 
    donorseg_simple + Assignment_flag + Total.Giving.Years, family = "binomial", 
    data = data_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.4932  -0.1410  -0.1220  -0.0873   3.5040  

Coefficients:
                             Estimate Std. Error z value             Pr(>|z|)    
(Intercept)                  -4.08066    0.24408 -16.718 < 0.0000000000000002 ***
Married_simple               -1.23973    0.08749 -14.170 < 0.0000000000000002 ***
No_of_Children                0.71530    0.05765  12.407 < 0.0000000000000002 ***
donorseg_simpleCurrent Donor -0.04313    0.24679  -0.175               0.8613    
donorseg_simpleLapsed Donor  -0.60244    0.25434  -2.369               0.0179 *  
donorseg_simpleLapsing Donor -0.40470    0.26802  -1.510               0.1311    
donorseg_simpleLost Donor    -0.96219    0.24415  -3.941            0.0000811 ***
Assignment_flag               1.19911    0.11744  10.210 < 0.0000000000000002 ***
Total.Giving.Years            0.14559    0.00441  33.013 < 0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 9764.4  on 68404  degrees of freedom
Residual deviance: 7544.5  on 68396  degrees of freedom
  (173845 observations deleted due to missingness)
AIC: 7562.5

Number of Fisher Scoring iterations: 8
exp(donor_logit3$coefficients)
                 (Intercept)               Married_simple               No_of_Children 
                  0.01689628                   0.28946282                   2.04479757 
donorseg_simpleCurrent Donor  donorseg_simpleLapsed Donor donorseg_simpleLapsing Donor 
                  0.95778285                   0.54747539                   0.66717987 
   donorseg_simpleLost Donor              Assignment_flag           Total.Giving.Years 
                  0.38205405                   3.31714962                   1.15672540 
#training predictions for in sample preds 
preds_train <- predict(donor_logit3, newdata = data_train, type = "response") 

#test predicts for OOS (out of sample)
preds_test <- predict(donor_logit3, newdata = data_test, type = "response")

head(preds_train)
     196454      181024       64789       23864       86886      188620 
         NA 0.003806297          NA          NA          NA          NA 
head(preds_test)
          1           6          17          18          21          27 
0.047220650 0.764137284 0.931364886 0.001865082 0.040099014 0.188797205 
results_train <- data.frame(
  `truth` = data_train   %>% select(major_gifter) %>% 
    mutate(major_gifter = as.numeric(major_gifter)),
  `Class1` =  preds_train,
  `type` = rep("train",length(preds_train))
)

results_test <- data.frame(
  `truth` = data_test   %>% select(major_gifter) %>% 
    mutate(major_gifter = as.numeric(major_gifter)),
  `Class1` =  preds_test,
  `type` = rep("test",length(preds_test))
)

results <- bind_rows(results_train,results_test)

dim(results_train)
[1] 242250      3
dim(results_test)
[1] 80750     3
dim(results)
[1] 323000      3
library('plotROC')

p_plot <-
  ggplot(results,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 2.5,
           #Took the labelsize down to avoid cutoff
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 #We removed some of the cutoffs to avoid the mashup near the origin.

  #Changed the theme to avoid cutoff plot values.
  theme_classic(base_size = 14) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")
print(p_plot) 



p_train <-
  ggplot(results_train,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 3.5,
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 
  theme_minimal(base_size = 16) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")

p_test <-
  ggplot(results_test,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 3.5,
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 
  theme_minimal(base_size = 16) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")

#Calculating AUC of both
print(calc_auc(p_train)$AUC)
[1] 0.8823867
print(calc_auc(p_test)$AUC)
[1] 0.8713205

RIDGE


library('glmnet')
library('glmnetUtils')

ridge_fit1 <- cv.glmnet(HH.Lifetime.Giving ~ sex_fct + donorseg_fct + No_of_Children,
                       data = data_train,
                       alpha = 0)

#Alpha 0 sets the Ridge
print(ridge_fit1)
Call:
cv.glmnet.formula(formula = HH.Lifetime.Giving ~ sex_fct + donorseg_fct + 
    No_of_Children, data = data_train, alpha = 0)

Model fitting options:
    Sparse model matrix: FALSE
    Use model.frame: FALSE
    Number of crossvalidation folds: 10
    Alpha: 0
    Deviance-minimizing lambda: 1192.867  (+1 SE): 11928665
print(ridge_fit1$lambda.min)
[1] 1192.867
print(ridge_fit1$lambda.1se)
[1] 11928665

LASSO


#enet_mod <- cva.glmnet(dependent ~ indy1 + indy2,
#                       data = data,
#                       alpha = seq(0,1, by = 0.1))

#print(enet_mod)
#plot(enet_mod)

ELASTICNET


minlossplot(enet_mod, 
            cv.type = "min")
Error in minlossplot(enet_mod, cv.type = "min") : 
  object 'enet_mod' not found

Ridges plot - could be useful for plotting donations vs donor segment

ggplot(data = corrplot_data, aes(x = nmb_degree, y = HH.Lifetime.Giving)) + 
  geom_point(aplha = 1/10)+
  geom_smooth(method = "lm", color ="red") 
`geom_smooth()` using formula 'y ~ x'

Random Forest

rf_fit_donor <- randomForest(Lifetime.Giving ~ ., 
                       data = data_train,
                       type = classification,
                       mtry = 7,
                       na.action = na.roughfix,
                       ntree = 200,
                       importance=TRUE
                       )
Error in na.roughfix.data.frame(list(Lifetime.Giving = c(0, 0, 0, 0, 0,  : 
  na.roughfix only works for numeric or factor
LS0tDQp0aXRsZTogIkJST0NPREUgU3VtbWFyeSBTdGF0aXN0aWNzIg0KYXV0aG9yOiAiQWFyb24gV2lsbGlzLCBDYW5ub24gQnJvb2tlLCBKb3NodWEgSGVuZGVyc29uLCBSeWFuIFJhZGNsaWZmIg0Kc3VidGl0bGU6IEJVUzY5NiBGaW5hbCBQcm9qZWN0IHYxNA0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQoNCiMgUGxlYXNlIGxlYXZlIHRoaXMgY29kZSBjaHVuayBhcyBpcy4gSXQgbWFrZXMgc29tZSBzbGlnaHQgZm9ybWF0dGluZyBjaGFuZ2VzIHRvIGFsdGVyIHRoZSBvdXRwdXQgdG8gYmUgbW9yZSBhZXN0aGV0aWNhbGx5IHBsZWFzaW5nLiANCg0KbGlicmFyeSgna25pdHInKQ0KDQoNCiMgQ2hhbmdlIHRoZSBudW1iZXIgaW4gc2V0IHNlZWQgdG8geW91ciBvd24gZmF2b3JpdGUgbnVtYmVyDQpzZXQuc2VlZCgxODE4KQ0Kb3B0aW9ucyh3aWR0aD03MCkNCm9wdGlvbnMoc2NpcGVuPTk5KQ0KDQoNCiMgdGhpcyBzZXRzIHRleHQgb3V0cHV0dGVkIGluIGNvZGUgY2h1bmtzIHRvIHNtYWxsDQpvcHRzX2NodW5rJHNldCh0aWR5Lm9wdHM9bGlzdCh3aWR0aC53cmFwPTUwKSx0aWR5PVRSVUUsIHNpemUgPSAidnNtYWxsIikgIA0Kb3B0c19jaHVuayRzZXQobWVzc2FnZSA9IEZBTFNFLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgIyAiY2FjaGluZyIgc3RvcmVzIG9iamVjdHMgaW4gY29kZSBjaHVua3MgYW5kIG9ubHkgcmV3cml0ZXMgaWYgeW91IGNoYW5nZSB0aGluZ3MNCiAgICAgICAgICAgICAgIGNhY2hlID0gVFJVRSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAjIGF1dG9tYXRpY2FsbHkgZG93bmxvYWRzIGRlcGVuZGVuY3kgZmlsZXMNCiAgICAgICAgICAgICAgIGF1dG9kZXAgPSBUUlVFLA0KICAgICAgICAgICAgICAgIyANCiAgICAgICAgICAgICAgIGNhY2hlLmNvbW1lbnRzID0gRkFMU0UsDQogICAgICAgICAgICAgICAjIA0KICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLA0KICAgICAgICAgICAgICAgIyBjaGFuZ2UgZmlnLndpZHRoIGFuZCBmaWcuaGVpZ2h0IHRvIGNoYW5nZSB0aGUgY29kZSBoZWlnaHQgYW5kIHdpZHRoIGJ5IGRlZmF1bHQNCiAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDUuNSwgIA0KICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDQuNSwNCiAgICAgICAgICAgICAgIGZpZy5hbGlnbj0nY2VudGVyJykNCg0KDQpgYGANCg0KYGBge3Igc2V0dXAtMn0NCg0KIyBBbHdheXMgcHJpbnQgdGhpcyBvdXQgYmVmb3JlIHlvdXIgYXNzaWdubWVudA0Kc2Vzc2lvbkluZm8oKQ0KZ2V0d2QoKQ0KDQpgYGANCg0KDQo8IS0tICMjIyBzdGFydCBhbnN3ZXJpbmcgeW91ciBwcm9ibGVtIHNldCBoZXJlIC0tPg0KPCEtLSBZb3UgbWF5IGV4cG9ydCB5b3VyIGhvbWV3b3JrIGluIGVpdGhlciBodG1sIG9yIHBkZiwgd2l0aCB0aGUgZm9ybWVyIHVzdWFsbHkgYmVpbmcgZWFzaWVyLiANCiAgICAgVG8gZXhwb3J0IG9yIGNvbXBpbGUgeW91ciBSbWQgZmlsZTogY2xpY2sgYWJvdmUgb24gJ0tuaXQnIHRoZW4gJ0tuaXQgdG8gSFRNTCcgLS0+DQo8IS0tIEJlIHN1cmUgdG8gc3VibWl0IGJvdGggeW91ciAuUm1kIGZpbGUgYW5kIHRoZSBjb21waWxlZCAuaHRtbCBvciAucGRmIGZpbGUgZm9yIGZ1bGwgY3JlZGl0IC0tPg0KDQoNCmBgYHtyIHNldHVwLTN9DQoNCiMgbG9hZCBhbGwgeW91ciBsaWJyYXJpZXMgaW4gdGhpcyBjaHVuayANCmxpYnJhcnkoJ3RpZHl2ZXJzZScpDQpsaWJyYXJ5KCJmcyIpDQpsaWJyYXJ5KCdoZXJlJykNCmxpYnJhcnkoJ2RwbHlyJykNCmxpYnJhcnkoJ3RpZHl2ZXJzZScpDQpsaWJyYXJ5KCdnZ3Bsb3QyJykNCmxpYnJhcnkoJ2dncmVwZWwnKQ0KbGlicmFyeSgnZ2d0aGVtZXMnKQ0KbGlicmFyeSgnZm9yY2F0cycpDQpsaWJyYXJ5KCdyc2FtcGxlJykNCmxpYnJhcnkoJ2x1YnJpZGF0ZScpDQpsaWJyYXJ5KCdnZ3RoZW1lcycpDQpsaWJyYXJ5KCdrYWJsZUV4dHJhJykNCmxpYnJhcnkoJ3Bhc3RlY3MnKQ0KbGlicmFyeSgndmlyaWRpcycpDQpsaWJyYXJ5KCdwbG90bHknKQ0KbGlicmFyeSgndGlkeXF1YW50JykNCmxpYnJhcnkoJ3NjYWxlcycpDQpsaWJyYXJ5KCJnZGF0YSIpDQoNCiMgbm90ZSwgZG8gbm90IHJ1biBpbnN0YWxsLnBhY2thZ2VzKCkgaW5zaWRlIGEgY29kZSBjaHVuay4gaW5zdGFsbCB0aGVtIGluIHRoZSBjb25zb2xlIG91dHNpZGUgb2YgYSBjb2RlIGNodW5rLiANCg0KYGBgDQoNCg0KDQojIyBQYXJ0IDEgLSBGaW5hbCBQcm9qZWN0IENsZWFuaW5nIGFuZCBTdW1tYXJ5IFN0YXRpc3RpY3MgDQoNCjFhKSBMb2FkaW5nIGRhdGENCg0KYGBge3J9DQoNCiNSZWFkaW5nIHRoZSBkYXRhIGluIGFuZCBkb2luZyBtaW5vciBpbml0aWFsIGNsZWFuaW5nIGluIHRoZSBmdW5jdGlvbiBjYWxsDQojUmVwcm9kdWNpYmxlIGRhdGEgYW5hbHlzaXMgc2hvdWxkIGF2b2lkIGFsbCBhdXRvbWF0aWMgc3RyaW5nIHRvIGZhY3RvciBjb252ZXJzaW9ucy4NCiNzdHJpcC53aGl0ZSByZW1vdmVzIHdoaXRlIHNwYWNlIA0KI25hLnN0cmluZ3MgaXMgYSBzdWJzdGl0dXRpb24gc28gYWxsIHRoYXQgaGF2ZSAiIiB3aWxsID0gbmENCmRhdGEgPC0gcmVhZC5jc3YoaGVyZTo6aGVyZSgiZmluYWxfcHJvamVjdCIsICJkb25vcl9kYXRhLmNzdiIpLA0KICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsDQogICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncyA9ICIiKQ0KDQoNCg0KYGBgDQoNCg0KMWIpIEZpeGluZyB0aGUgd29ua3kgRE9CICYgRGF0YSBjbGVhbnVwDQoNCmBgYHtyfQ0KDQojKEJpcnRoZGF0ZSBhbmQgQWdlLCBJRCBhcyBhIG51bWJlcilhZGRpbmcgRE9CIChBZ2UvU3BvdXNlIEFnZSkgaW4geWVhcnMgY29sdW1ucyBhbmQgYWRkaW5nIHR3byBmaWVsZHMgZm9yIGFzc2lnbm1lbnQgYW5kIG51bWJlciBvZiBjaGlsZHJlbiBhbmQgbnVtYmVyIG9mIGRlZ3JlZXMNCmRhdGFjbGVhbiA8LSBkYXRhICU+JQ0KICBtdXRhdGUoQmlydGhkYXRlID0gaWZlbHNlKEJpcnRoZGF0ZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBCaXJ0aGRhdGUpKSAlPiUNCiAgbXV0YXRlKEJpcnRoZGF0ZSA9IG1keShCaXJ0aGRhdGUpKSAlPiUNCiAgbXV0YXRlKEFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IEJpcnRoZGF0ZSwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUNCiAgbXV0YXRlKFNwb3VzZS5CaXJ0aGRhdGUgPSBpZmVsc2UoU3BvdXNlLkJpcnRoZGF0ZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBTcG91c2UuQmlydGhkYXRlKSkgJT4lDQogIG11dGF0ZShTcG91c2UuQmlydGhkYXRlID0gbWR5KFNwb3VzZS5CaXJ0aGRhdGUpKSAlPiUNCiAgbXV0YXRlKFNwb3VzZS5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBTcG91c2UuQmlydGhkYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUNCiAgbXV0YXRlKElEID0gYXMubnVtZXJpYyhJRCkpICU+JSANCiAgbXV0YXRlKEFzc2lnbm1lbnRfZmxhZyA9IGlmZWxzZShpcy5uYShBc3NpZ25tZW50Lk51bWJlciksIDAsMSkpICU+JSANCiAgbXV0YXRlKCBOb19vZl9DaGlsZHJlbiA9IGlmZWxzZShpcy5uYShDaGlsZC4xLklEKSwwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShDaGlsZC4yLklEKSwxLDIpKSklPiUNCiBtdXRhdGUoSUQgPSBhcy5udW1lcmljKElEKSkgJT4lIA0KICAgIG11dGF0ZSggbm1iX2RlZ3JlZSA9IGlmZWxzZShpcy5uYShEZWdyZWUuVHlwZS4xKSwwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShEZWdyZWUuVHlwZS4yKSwxLDIpKSkNCiNjb25mZXJyYWwgZGF0ZXMNCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lDQogIA0KICBtdXRhdGUoQ29uZmVycmFsLkRhdGUuMSA9IGlmZWxzZShDb25mZXJyYWwuRGF0ZS4xID09ICIwMDAxLTAxLTAxIiwgTkEsIENvbmZlcnJhbC5EYXRlLjEpKSAlPiUNCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjEgPSBtZHkoQ29uZmVycmFsLkRhdGUuMSkpICU+JQ0KICBtdXRhdGUoQ29uZmVycmFsLkRhdGUuMS5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBDb25mZXJyYWwuRGF0ZS4xLCBlbmQ9U3lzLkRhdGUoKSkvZHVyYXRpb24obj0xLCB1bml0PSJ5ZWFycyIpKSkpICU+JQ0KICANCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjIgPSBpZmVsc2UoQ29uZmVycmFsLkRhdGUuMiA9PSAiMDAwMS0wMS0wMSIsIE5BLCBDb25mZXJyYWwuRGF0ZS4yKSkgJT4lDQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4yID0gbWR5KENvbmZlcnJhbC5EYXRlLjIpKSAlPiUNCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjIuQWdlID0gYXMubnVtZXJpYyhmbG9vcihpbnRlcnZhbChzdGFydD0gQ29uZmVycmFsLkRhdGUuMiwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUNCiAgDQogIG11dGF0ZShMYXN0LkNvbnRhY3QuQnkuQW55b25lID0gaWZlbHNlKExhc3QuQ29udGFjdC5CeS5BbnlvbmUgPT0gIjAwMDEtMDEtMDEiLCBOQSwgTGFzdC5Db250YWN0LkJ5LkFueW9uZSkpICU+JQ0KICBtdXRhdGUoTGFzdC5Db250YWN0LkJ5LkFueW9uZSA9IG1keShMYXN0LkNvbnRhY3QuQnkuQW55b25lKSkgJT4lDQogIG11dGF0ZShMYXN0LkNvbnRhY3QuQWdlID0gYXMubnVtZXJpYyhmbG9vcihpbnRlcnZhbChzdGFydD0gTGFzdC5Db250YWN0LkJ5LkFueW9uZSwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUNCiAgDQogbXV0YXRlKEhILkZpcnN0LkdpZnQuRGF0ZSA9IGlmZWxzZShISC5GaXJzdC5HaWZ0LkRhdGUgPT0gIjAwMDEtMDEtMDEiLCBOQSwgSEguRmlyc3QuR2lmdC5EYXRlKSkgJT4lDQogIG11dGF0ZShISC5GaXJzdC5HaWZ0LkRhdGUgPSBtZHkoSEguRmlyc3QuR2lmdC5EYXRlKSkgJT4lDQptdXRhdGUoSEguRmlyc3QuR2lmdC5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBISC5GaXJzdC5HaWZ0LkRhdGUsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkNCg0KI21ham9yIGdpZnQgDQpkYXRhY2xlYW4gPC0gDQogIGRhdGFjbGVhbiAlPiUgDQogIG11dGF0ZShtYWpvcl9naWZ0ZXIgPSBpZmVsc2UoTGlmZXRpbWUuR2l2aW5nID4gNTAwMDAsIDEsMCkgJT4lIGZhY3RvciguLCBsZXZlbHMgPSBjKCIwIiwiMSIpKSkNCg0KDQojc3BsaXR0aW5nIHVwIHRoZSBhZ2UgaW50byByYW5nZXMgYW5kIGNyZWF0aW5nIGNhdGVnb3J5IGZvciBlYXN5IHZpc3VhbGl6YXRpb24gDQpkYXRhY2xlYW4gPC0gZGF0YWNsZWFuICU+JQ0KICBtdXRhdGUoYWdlX3JhbmdlID0gDQogICAgaWZlbHNlKEFnZSAlaW4lIDEwOjE5LCAiMTAgPCAyMCB5ZWFycyBvbGQiLA0KICAgIGlmZWxzZShBZ2UgJWluJSAyMDoyOSwgIjIwIDwgMzAgeWVhcnMgb2xkIiwgDQogICAgaWZlbHNlKEFnZSAlaW4lIDMwOjM5LCAiMzAgPCA0MCB5ZWFycyBvbGQiLA0KICAgIGlmZWxzZShBZ2UgJWluJSA0MDo0OSwgIjQwIDwgNTAgeWVhcnMgb2xkIiwNCiAgICBpZmVsc2UoQWdlICVpbiUgNTA6NTksICI1MCA8IDYwIHllYXJzIG9sZCIsDQogICAgaWZlbHNlKEFnZSAlaW4lIDYwOjY5LCAiNjAgPCA3MCB5ZWFycyBvbGQiLA0KICAgIGlmZWxzZShBZ2UgJWluJSA3MDo3OSwgIjcwIDwgODAgeWVhcnMgb2xkIiwNCiAgICBpZmVsc2UoQWdlICVpbiUgODA6ODksICI4MCA8IDkwIHllYXJzIG9sZCIsDQogICAgaWZlbHNlKEFnZSAlaW4lIDkwOjEyMCwgIjkwKyB5ZWFycyBvbGQiLA0KICAgIE5BKSkpKSkpKSkpKQ0KDQoNCiNzZWVpbmcgd2hhdCB3ZSBoYXZlDQp0YWJsZShkYXRhY2xlYW4kYWdlX3JhbmdlKQ0KIzUwLTYwIGlzIHRoZSBtb3N0IGNvbW1vbiBhZ2UgcmFuZ2UgDQoNCiNjcmVhdGluZyBhIHJlZ2lvbiBjb2x1bW4gdXNpbmcgdGhlIGNvdW50eSBkYXRhIGFuZCB0aGUgT01CIE1TQSAoTWV0cm9wb2xpdGFuIFN0YXRpc3RpY2FsIEFyZWEpIGRlZmluaXRpb25zDQoNCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lDQogIG11dGF0ZShyZWdpb24gPSANCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gTHVpcyBPYmlzcG8iICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiS2VybiIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gQmVybmFyZGlubyIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW50YSBCYXJiYXJhIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIlZlbnR1cmEiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiTG9zIEFuZ2VsZXMiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiT3JhbmdlIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIlJpdmVyc2lkZSIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gRGllZ28iICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiSW1wZXJpYWwiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiS2luZyIgJiBTdGF0ZSA9PSAiV0EiLCAiU2VhdHRsZSIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiU25vaG9taXNoIiAmIFN0YXRlID09ICJXQSIsICJTZWF0dGxlIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJQaWVyY2UiICYgU3RhdGUgPT0gIldBIiwgIlNlYXR0bGUiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIkNsYWNrYW1hcyIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIkNvbHVtYmlhIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiTXVsdG5vbWFoIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiV2FzaGluZ3RvbiIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIllhbWhpbGwiICYgU3RhdGUgPT0gIk9SIiwgIlBvcnRsYW5kIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJDbGFyayIgJiBTdGF0ZSA9PSAiV0EiLCAiUG9ydGxhbmQiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIlNrYW1hbmlhIiAmIFN0YXRlID09ICJXQSIsICJQb3J0bGFuZCIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiRGVudmVyIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIkFyYXBhaG9lIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIkplZmZlcnNvbiIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJBZGFtcyIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJEb3VnbGFzIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIkJyb29tZmllbGQiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsICAgIA0KICAgIGlmZWxzZShDb3VudHkgPT0gIkVsYmVydCIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJQYXJrIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIkNsZWFyIENyZWVrIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIkFsYW1lZGEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJDb250cmEgQ29zdGEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJNYXJpbiIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIk1vbnRlcmV5IiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiTmFwYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBCZW5pdG8iICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gRnJhbmNpc2NvIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIE1hdGVvIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiU2FudGEgQ2xhcmEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW50YSBDcnV6IiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiU29sYW5vIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiU29ub21hIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsDQogICAgICAgICAgIE5BKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpDQoNCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lDQogIG11dGF0ZShyZWdpb24gPSANCiAgICBpZmVsc2UoQ291bnR5ID09ICJLaW5ncyIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIlF1ZWVucyIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIk5ldyBZb3JrIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiQnJvbngiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJSaWNobW9uZCIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIldlc3RjaGVzdGVyIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiQmVyZ2VuIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiSHVkc29uIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiUGFzc2FpYyIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIlB1dG5hbSIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIlJvY2tsYW5kIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiU3VmZm9sayIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIk5hc3NhdSIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIk1pZGRsZXNleCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIk1vbm1vdXRoIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiT2NlYW4iICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJTb21lcnNldCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLA0KICAgIGlmZWxzZShDb3VudHkgPT0gIkVzc2V4IiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsDQogICAgaWZlbHNlKENvdW50eSA9PSAiVW5pb24iICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJNb3JyaXMiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJTdXNzZXgiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJIdW50ZXJkb24iICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwNCiAgICBpZmVsc2UoQ291bnR5ID09ICJQaWtlIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsDQogICAgcmVnaW9uKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpDQoNCg0KIyBjb2RlIG5vciBjYWwgcmVnaW9uIGFzIGFsbCBvdGhlcnMgaW4gQ0Egbm90IGFscmVhZHkgZGVmaW5lZA0KDQpkYXRhY2xlYW4gPC0gZGF0YWNsZWFuICU+JQ0KICBtdXRhdGUocmVnaW9uID0gDQogICAgaWZlbHNlKFN0YXRlID09ICJDQSIgJiBpcy5uYShyZWdpb24pID09IFRSVUUsICJOb3IgQ2FsIiwgcmVnaW9uKSkNCg0KDQojUmVtb3ZpbmcgQ29sdW1ucyB0aGF0IHByb3ZpZGUgbm8gYmVuZWZpdCANCg0KZGF0YWNsZWFuIDwtIHN1YnNldChkYXRhY2xlYW4sc2VsZWN0ID0gLWMoQXNzaWdubWVudC5OdW1iZXINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5oYXMuSGlzdG9yaWNhbC5NbmdyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFN1ZmZpeA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LkRhdGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5NYW5hZ2VyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuUm9sZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LlRpdGxlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuU3RhdHVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFN0cmF0ZWd5DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFByb2dyZXNzLkxldmVsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuR3JvdXANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5DYXRlZ29yeQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxGdW5kaW5nLk1ldGhvZA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsRXhwZWN0ZWQuQm9vay5EYXRlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxRdWFsaWZpY2F0aW9uLkFtb3VudA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsRXhwZWN0ZWQuQm9vay5BbW91bnQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEV4cGVjdGVkLkJvb2suRGF0ZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsSGFyZC5HaWZ0LlRvdGFsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxTb2Z0LkNyZWRpdC5Ub3RhbA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsVG90YWwuQXNzaWdubWVudC5HaWZ0cw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsTm8ub2YuUGxlZGdlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUHJvcG9zYWwuLg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUHJvcG9zYWwuTm90ZXMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEhILkxpZmUuU3BvdXNlLkNyZWRpdA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsTGFzdC5Db250YWN0LkJ5Lk1hbmFnZXINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFguLm9mLkNvbnRhY3RzLkJ5Lk1hbmFnZXINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLERvbm9yU2VhcmNoLlJhbmdlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxpV2F2ZS5SYW5nZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsV2VhbHRoRW5naW5lLlJhbmdlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQaGlsYW50aHJvcGljLkNvbW1pdG1lbnRzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpDQojY2xlYW5pbmcgdXAgemlwIGNvZGVzIHJlbW92aW5nIC00IGFmdGVyIA0KZGF0YWNsZWFuJFppcCA8LSBnc3ViKGRhdGFjbGVhbiRaaXAsIHBhdHRlcm49Ii0uKiIsIHJlcGxhY2VtZW50ID0gIiIpDQoNCiNhZGRpbmcgemlwIGNvZGUgZGF0YSBhbmQgY29sdW1uIA0KemlwIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAiU2FsYXJ5X1ppcGNvZGUuY3N2IiksDQogICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgc3RyaXAud2hpdGUgPSBUUlVFLA0KICAgICAgICAgICAgICAgICBuYS5zdHJpbmdzID0gIiIpDQoNCg0KI2FkZGluZyB6aXAgc2FsYXJ5IGNvbHVtbg0KZGF0YWNsZWFuIDwtZGF0YWNsZWFuICU+JQ0KICAgIG11dGF0ZSh6aXBjb2RlX3NscnkgPSBWTE9PS1VQKFppcCwgemlwLCBOQU1FLCBTMTkwMl9DMDNfMDAyRSkpDQoNCiNzbHJ5IHJhbmdlIA0KZGF0YWNsZWFuIDwtIGRhdGFjbGVhbiAlPiUNCiAgbXV0YXRlKHppcHNscnlfcmFuZ2UgPSANCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMTAwMDA6ODk5OTksICI5MEstOTlLIiwNCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgOTAwMDA6OTk5OTksICI5MEstOTlLIiwNCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMTAwMDAwOjE0OTk5OSwgIjEwMEstMTQ5SyIsIA0KICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAxNTAwMDA6MTk5OTk5LCAiMTUwSy0xOTlLIiwNCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMjAwMDAwOjI0OTk5OSwgIjIwMEstMjQ5SyIsDQogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDI1MDAwMDoyOTk5OTksICIyNTBLLTI5OUsiLA0KICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAzMDAwMDA6MzQ5OTk5LCAiMzAwSy0zNDlLIiwNCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMzUwMDAwOjM5OTk5OSwgIjM1MEstMzk5SyIsDQogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDQwMDAwMDo0OTk5OTksICI0MDBLLTQ5OUsiLA0KICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSA1MDAwMDA6OTk5OTk5LCAiNTAwSy05OTlLIiwNCiAgICBOQSkpKSkpKSkpKSkpDQoNCnN1bShpcy5uYShkYXRhY2xlYW4kemlwY29kZV9zbHJ5KSkNCg0KI2FkZGluZyBzY2hvbGFyc2hpcCBkYXRhICh5L24pDQpzY2hsciA8LSByZWFkLmNzdihoZXJlOjpoZXJlKCJmaW5hbF9wcm9qZWN0IiwgInNjaG9sYXJzaGlwLmNzdiIpLA0KICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsDQogICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncyA9ICIiKQ0KDQojYWRkaW5nIHNjaG9sYXJzaGlwIGNvbHVtbg0KZGF0YWNsZWFuIDwtZGF0YWNsZWFuICU+JQ0KICAgIG11dGF0ZShzY2hvbGFyc2hpcCA9IFZMT09LVVAoSUQsIHNjaGxyLCBJRCwgU0NIT0xBUlNISVApKSANCg0KI3JlcGxhY2luZyBOQSB3aXRoIDAgDQogZGF0YWNsZWFuJHNjaG9sYXJzaGlwIDwtIHJlcGxhY2VfbmEoZGF0YWNsZWFuJHNjaG9sYXJzaGlwLCcwJykNCiANCiNyZXBsYWNpbmcgWSB3aXRoIDEgDQpkYXRhY2xlYW4kc2Nob2xhcnNoaXA8LWlmZWxzZShkYXRhY2xlYW4kc2Nob2xhcnNoaXA9PSJZIiwxLDApDQoNCiNjaGVja2luZyBob3cgbWFueSBhcmUgTg0KdGFibGUoZGF0YWNsZWFuJHNjaG9sYXJzaGlwKQ0KDQoNCiNjaGVja2luZyBhbmQgZGVsZXRpbmcgc2Nob2xhcnNoaXAgY29sdW1uIA0KY2xhc3MoZGF0YWNsZWFuJHNjaGxyX2ZjdCkNCmRhdGFjbGVhbiA9IHN1YnNldChkYXRhY2xlYW4sIHNlbGVjdCA9IC1jKHNjaG9sYXJzaGlwKSkNCiAgDQojY2hlY2tpbmcgZm9yIGR1cGxpY2F0ZXMgTiA+MSBpbmRpY2F0ZXMgYSByZWNvcmRzIHZhbHVlcyBhcmUgaW4gdGhlIGZpbGUgdHdpY2UgDQpkYXRhY2xlYW4gJT4lIGdyb3VwX2J5KElEKSAlPiUgY291bnQoKSAlPiUgYXJyYW5nZShkZXNjKG4pKQ0KDQojcmVtb3ZpbmcgZHVwbGljYXRlZCByZWNvcmRzDQoNCmRhdGFjbGVhbiA8LSB1bmlxdWUoZGF0YWNsZWFuKQ0KDQojbiA9IDEgbm8gSUQgd2l0aCBtdWx0aXBsZSByZWNvcmRzIGNsZWFuZWQgb2YgZHVwZXMNCmRhdGFjbGVhbiAlPiUgZ3JvdXBfYnkoSUQpICU+JSBjb3VudCgpICU+JSBhcnJhbmdlKGRlc2MobikpDQoNCmBgYA0KDQoNCjFkIENyZWF0aW5nIG1hbnkgbWFueSBmYWN0b3IgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQpkYXRhY2xlYW4gPC0gDQogIGRhdGFjbGVhbiAlPiUgDQogICNTRVgNCiAgbXV0YXRlKHNleF9mY3QgPSANCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNleCksDQpzZXhfc2ltcGxlID0gDQogICAgZmN0X2x1bXBfbihTZXgsIG4gPSA0KSwNCiNNQVJSSUVEDQptYXJyaWVkX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWFycmllZCksDQogICNET05PUiBTRUdNRU5UDQogIGRvbm9yc2VnX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRG9ub3IuU2VnbWVudCksDQogICAgICAgICBkb25vcnNlZ19zaW1wbGUgPSANCiAgICAgICAgICAgZmN0X2x1bXBfbihEb25vci5TZWdtZW50LCBuID0gNCksDQogICNDT05UQUNUIFJVTEUNCiAgICAgICAgIGNvbnRhY3RfZmN0ID0gDQogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDb250YWN0LlJ1bGVzKSwNCiAgICAgICAgIGNvbnRhY3Rfc2ltcGxlID0gDQogICAgICAgICAgIGZjdF9sdW1wX24oQ29udGFjdC5SdWxlcywgbiA9IDQpLA0KICAjU1BPVVNFIE1BSUwNCiAgICAgICAgIHNwb21haWxfZmN0ID0gDQogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTcG91c2UuTWFpbC5SdWxlcyksDQogICAgICAgICBzcG9tYWlsX3NpbXBsZSA9IA0KICAgICAgICAgICBmY3RfbHVtcF9uKFNwb3VzZS5NYWlsLlJ1bGVzLCBuID0gNCksDQogICNKT0IgVElUTEUNCiAgICAgICAgIGpvYnRpdGxlX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoSm9iLlRpdGxlKSwNCiAgICAgICAgIGpvYnRpdGxlX3NpbXBsZSA9IA0KICAgICAgICAgICBmY3RfbHVtcF9uKEpvYi5UaXRsZSwgbiA9IDUpLA0KICAjREVHUkVFIFRZUEUgMQ0KICAgICAgICAgZGVnMV9mY3QgPSANCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKERlZ3JlZS5UeXBlLjEpLA0KICAgICAgICAgZGVnMV9zaW1wbGUgPSANCiAgICAgICAgICAgZmN0X2x1bXBfbihEZWdyZWUuVHlwZS4xLCBuID0gNSksDQogICNERUdSRUUgVFlQRSAyDQogICAgICAgICBkZWcyX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRGVncmVlLlR5cGUuMiksDQogICAgICAgICBkZWcyX3NpbXBsZSA9IA0KICAgICAgICAgICBmY3RfbHVtcF9uKERlZ3JlZS5UeXBlLjIsIG4gPSA1KSwNCiAgI01BSk9SIDENCiAgICAgICAgIG1hajFfZmN0ID0gDQogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShNYWpvci4xKSwNCiAgICAgICAgIG1hajFfc2ltcGxlID0gDQogICAgICAgICAgIGZjdF9sdW1wX24oTWFqb3IuMSwgbiA9IDUpLA0KICAjTUFKT1IgMg0KICAgICAgICAgbWFqMl9mY3QgPSANCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1ham9yLjIpLA0KICAgICAgICAgbWFqMl9zaW1wbGUgPSANCiAgICAgICAgICAgZmN0X2x1bXBfbihNYWpvci4yLCBuID0gNSksDQogICNNSU5PUiAxDQogICAgICAgICBtaW4xX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWlub3IuMSksDQogICAgICAgICBtaW4xX3NpbXBsZSA9IA0KICAgICAgICAgICBmY3RfbHVtcF9uKE1pbm9yLjEsIG4gPSA1KSwNCiAgI01JTk9SIDINCiAgICAgICAgIG1pbjJfZmN0ID0gDQogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShNaW5vci4yKSwNCiAgICAgICAgIG1pbjJfc2ltcGxlID0gDQogICAgICAgICAgIGZjdF9sdW1wX24oTWlub3IuMiwgbiA9IDUpLA0KICAjU0NIT09MIDENCiAgICAgICAgIHNjaG9vbDFfZmN0ID0gDQogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTY2hvb2wuMSksDQogICAgICAgICBzY2hvb2wxX3NpbXBsZSA9IA0KICAgICAgICAgICBmY3RfbHVtcF9uKFNjaG9vbC4xLCBuID0gNSksDQogICNTQ0hPT0wgMg0KICAgICAgICAgc2Nob29sMl9mY3QgPSANCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNjaG9vbC4yKSwNCiAgICAgICAgIHNjaG9vbDJfc2ltcGxlID0gDQogICAgICAgICAgIGZjdF9sdW1wX24oU2Nob29sLjIsIG4gPSA1KSwNCiAgI0lOU1RJVFVUSU9OIFRZUEUNCiAgICAgICAgIGluc3R0eXBlX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoSW5zdGl0dXRpb24uVHlwZSksDQogICAgICAgICBpbnN0dHlwZV9zaW1wbGUgPSANCiAgICAgICAgICAgZmN0X2x1bXBfbihJbnN0aXR1dGlvbi5UeXBlLCBuID0gNSksDQogICNFWFRSQUNVUlJJQ1VMQVINCiAgICAgICAgIGV4dHJhX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRXh0cmFjdXJyaWN1bGFyKSwNCiAgICAgICAgIGV4dHJhX3NpbXBsZSA9IA0KICAgICAgICAgICBmY3RfbHVtcF9uKEV4dHJhY3VycmljdWxhciwgbiA9IDUpLA0KICAjSEggRklSU1QgR0lGVCBGVU5EDQogICAgICAgICBoaGZpcnN0Z2lmdF9mY3QgPSANCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKEhILkZpcnN0LkdpZnQuRnVuZCksDQogICAgICAgICBoaGZpcnN0Z2lmdF9zaW1wbGUgPSANCiAgICAgICAgICAgZmN0X2x1bXBfbihISC5GaXJzdC5HaWZ0LkZ1bmQsIG4gPSA1KSwNCiNDSElMRCAxIEVOUk9MTCBTVEFUVVMNCiAgICAgICAgIGNoMV9lbnJvbGxfZmN0ID0gDQogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4xLkVucm9sbC5TdGF0dXMpLA0KICAgICAgICAgY2gxX2Vucm9sbF9zaW1wbGUgPSANCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLkVucm9sbC5TdGF0dXMsIG4gPSA0KSwNCiNDSElMRCAxIE1BSk9SDQogICAgICAgICBjaDFfbWFqX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5NYWpvciksDQogICAgICAgICBjaDFfbWFqX3NpbXBsZSA9IA0KICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuTWFqb3IsIG4gPSA0KSwNCiNDSElMRCAxIE1JTk9SDQogICAgICAgICBjaDFfbWluX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5NaW5vciksDQogICAgICAgICBjaDFfbWluX3NpbXBsZSA9IA0KICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuTWlub3IsIG4gPSA0KSwNCiNDSElMRCAxIFNDSE9PTA0KICAgICAgICAgY2gxX3NjaG9vbF9mY3QgPSANCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjEuU2Nob29sKSwNCiAgICAgICAgIGNoMV9zY2hvb2xfc2ltcGxlID0gDQogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMS5TY2hvb2wsIG4gPSA0KSwNCiNDSElMRCAxIEZFRURFUg0KICAgICAgICAgY2gxX2ZlZWRlcl9mY3QgPSANCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjEuRmVlZGVyLlNjaG9vbCksDQogICAgICAgICBjaDFfZmVlZGVyX3NpbXBsZSA9IA0KICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuRmVlZGVyLlNjaG9vbCwgbiA9IDQpLA0KI0NISUxEIDIgRU5ST0xMIFNUQVRVUw0KICAgICAgICAgY2gxX2Vucm9sbF9mY3QgPSANCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuRW5yb2xsLlN0YXR1cyksDQogICAgICAgICBjaDJfZW5yb2xsX3NpbXBsZSA9IA0KICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjIuRW5yb2xsLlN0YXR1cywgbiA9IDQpLA0KI0NISUxEIDIgTUFKT1INCiAgICAgICAgIGNoMl9tYWpfZmN0ID0gDQogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4yLk1ham9yKSwNCiAgICAgICAgIGNoMl9tYWpfc2ltcGxlID0gDQogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5NYWpvciwgbiA9IDQpLA0KI0NISUxEIDIgTUlOT1INCiAgICAgICAgIGNoMl9taW5fZmN0ID0gDQogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4yLk1pbm9yKSwNCiAgICAgICAgIGNoMl9taW5fc2ltcGxlID0gDQogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5NaW5vciwgbiA9IDQpLA0KI0NISUxEIDIgU0NIT09MDQogICAgICAgICBjaDJfc2Nob29sX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMi5TY2hvb2wpLA0KICAgICAgICAgY2gyX3NjaG9vbF9zaW1wbGUgPSANCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4yLlNjaG9vbCwgbiA9IDQpLA0KI0NISUxEIDIgRkVFREVSDQogICAgICAgICBjaDJfZmVlZGVyX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMi5GZWVkZXIuU2Nob29sKSwNCiAgICAgICAgIGNoMl9mZWVkZXJfc2ltcGxlID0gDQogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5GZWVkZXIuU2Nob29sLCBuID0gNCksDQogICAgKQ0KDQoNCg0KI2NoZWNraW5nIHRvIHNlZSBpZiBpdHMgYSBmYWN0b3INCiNjbGFzcyhkYXRhY2xlYW4kc2V4X2ZjdCkNCiNjbGFzcyhkYXRhY2xlYW4kZG9ub3JzZWdfZmN0KQ0KI2NsYXNzKGRhdGFjbGVhbiRjb250YWN0X2ZjdCkNCiNjbGFzcyhkYXRhY2xlYW4kc3BvbWFpbF9mY3QpDQoNCiNjaGVja2luZyBsZXZlbHMNCiNsZXZlbHMoZGF0YWNsZWFuJHNleF9zaW1wbGUpDQojbGV2ZWxzKGRhdGFjbGVhbiRkb25vcnNlZ19zaW1wbGUpDQojbGV2ZWxzKGRhdGFjbGVhbiRjb250YWN0X3NpbXBsZSkNCiNsZXZlbHMoZGF0YWNsZWFuJHNwb21haWxfc2ltcGxlKQ0KI2xldmVscyhkYXRhY2xlYW4kaGhmaXJzdGdpZnRfc2ltcGxlKQ0KDQojY3JlYXRpbmcgYSB0YWJsZSBhZ2FpbnN0IFNleCBjb2x1bW4gDQojdGFibGUoZGF0YWNsZWFuJHNleF9mY3QsIGRhdGFjbGVhbiRzZXhfc2ltcGxlKQ0KDQoNCmBgYA0KDQpSZWdpb24gQW5hbHlzaXMNCg0KYGBge3J9DQojZ3JvdXBpbmcgYnkgcmVnaW9uIGFuZCBhbmFseXppbmcgDQpkYXRhY2xlYW4gJT4lDQogIGdyb3VwX2J5KHJlZ2lvbikgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IGxlbmd0aChyZWdpb24pLA0KICAgICAgICAgICAgbWVhbl90b3RhbF9naXYgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZykpICU+JQ0KICBhcnJhbmdlKC1Db3VudCkgJT4lDQogIGZpbHRlcihDb3VudCA+PSAxMDApICU+JQ0KICBtdXRhdGUobWVhbl90b3RhbF9naXYgPSBkb2xsYXIobWVhbl90b3RhbF9naXYpKSAlPiUNCiAga2FibGUoY29sLm5hbWVzID0gYygiUmVnaW9uIiwgIkNvdW50IiwgIk1lYW4gSEggTGlmZXRpbWUgR2l2aW5nIiksIGFsaWduPXJlcCgnYycsIDMpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwNCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikNCiAgDQoNCmBgYA0KDQoNCkRvbm9yU2VnbWVudCBBbmFseXNpcw0KDQpgYGB7cn0NCiNncm91cGluZyBieSBkb25vcnNlZ21lbnQgYW5kIGFuYWx5emluZyANCmRhdGFjbGVhbiAlPiUNCiAgZ3JvdXBfYnkoRG9ub3IuU2VnbWVudCkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IGxlbmd0aChEb25vci5TZWdtZW50KSwNCiAgICAgICAgICAgIG1lYW5fdG90YWxfZ2l2ID0gbWVhbihISC5MaWZldGltZS5HaXZpbmcpKSAlPiUNCiAgYXJyYW5nZSgtQ291bnQpICU+JQ0KICBmaWx0ZXIoQ291bnQgPj0gMTAwKSAlPiUNCiAgI2FkZGVkIHNjYWxlcyBwYWNrYWdlIHRvIGhhdmUgdGhlIHZhbHVlcyBzaG93IGluIGRvbGxhciANCiAgbXV0YXRlKG1lYW5fdG90YWxfZ2l2ID0gKGRvbGxhcihtZWFuX3RvdGFsX2dpdikpKSAlPiUNCiAga2FibGUoY29sLm5hbWVzID0gYygiRG9ub3IgU2VnbWVudCIsICJDb3VudCIsICJNZWFuIEhIIExpZmV0aW1lIEdpdmluZyIpLCBhbGlnbj1yZXAoJ2MnLCAzKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksDQogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpDQogIA0KDQpgYGANCg0KRmlyc3QgZ2lmdCBzaXplIA0KYGBge3J9DQphcSA8LSBxdWFudGlsZShkYXRhY2xlYW4kSEguRmlyc3QuR2lmdC5BbW91bnQsIHByb2JzID0gYyguMjUsLjUwLC43NSwuOSwuOTkpLCBuYS5ybSA9IFRSVUUpDQoNCmFxIDwtIGFzLmRhdGEuZnJhbWUoYXEpDQoNCmFxJGFxIDwtIGRvbGxhcihhcSRhcSkNCg0KYXEgJT4lDQogIGthYmxlKGNvbC5uYW1lcyA9ICJRdWFudGlsZSIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLA0KICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBGKQ0KICANCg0KYGBgDQpDb25zZWN1dGl2ZSBnaXZpbmcNCmBgYHtyfQ0KI2NvbnNlY3V0aXZlIHllYXJzIG9mIGdpdmluZyANCmRhdGFjbGVhbiAlPiUNCiAgZmlsdGVyKE1heC5Db25zZWMuRmlzY2FsLlllYXJzID4gMCkgJT4lDQogIGdncGxvdChhZXMoTWF4LkNvbnNlYy5GaXNjYWwuWWVhcnMpKSArIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiIzAwMjg0NSIsIGJpbnMgPSAyMCkgKyANCiAgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKw0KICBnZ3RpdGxlKCJDb25zZWN1dGl2ZSBZZWFycyBvZiBHaXZpbmcgRGlzdHJpYnV0aW9uIikgKyANCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTIwLDIpKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMDAwMDAwMCw1MDAwKSkgDQoNCg0KDQpgYGANCg0KTGlmZXRpbWUgZ2l2aW5nIGJhc2VkIG9uIG51bWJlciBvZiBjaGlsZHJlbiANCg0KYGBge3J9DQpkYXRhY2xlYW4gJT4lDQogIGZpbHRlcihISC5MaWZldGltZS5HaXZpbmcgPD0gMTAwMDApICU+JQ0KICBmaWx0ZXIoSEguTGlmZXRpbWUuR2l2aW5nID4gMCkgJT4lDQogIG11dGF0ZShgTm9fb2ZfQ2hpbGRyZW5gID0gYXMuZmFjdG9yKGBOb19vZl9DaGlsZHJlbmApKSAlPiUNCiAgZ2dwbG90KGFlcyhISC5MaWZldGltZS5HaXZpbmcsIGZpbGwgPSBgTm9fb2ZfQ2hpbGRyZW5gKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzApICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKw0KICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMDAwMDAsMTAwMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEwMDAwMDAwMCw1MDAwKSkgKw0KICBnZ3RpdGxlKCJHaXZpbmcgZGlzdHJpYnV0aW9uIGFuZCBudW1iZXIgb2YgY2hpbGRyZW4iKSsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjMDAyODQ1IiwgIiMwMGNmY2MiLCAiI2ZmOTk3MyIpKQ0KDQoNCg0KYGBgDQoNCg0KTWVhbiwgTWVkaWFuLCBhbmQgQ291bnQgb2YgR2l2aW5nIGluIEFnZSBSYW5nZXMgDQoNCmBgYHtyfQ0KDQphZ2VfcmFuZ2VfZ2l2aW5nIDwtIGRhdGFjbGVhbiAlPiUNCiAgZ3JvdXBfYnkoYWdlX3JhbmdlKSAlPiUNCiAgc3VtbWFyaXNlKGF2Z19naXZpbmcgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIG1lZF9naXZpbmcgPSBtZWRpYW4oSEguTGlmZXRpbWUuR2l2aW5nLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgYW1vdW50X29mX3Blb3BsZV9pbl9hZ2VfcmFuZ2UgPSBuKCkpDQoNCg0KZ2xpbXBzZShhZ2VfcmFuZ2VfZ2l2aW5nKQ0KDQpgYGANCg0KDQoNCg0KDQojIyBQYXJ0IDINCg0KMmEpIFBsb3R0aW5nIGF2ZXJhZ2UgZ2l2aW5nIGJ5IGFnZSByYW5nZSANCg0KDQpgYGB7cn0NCg0KYWdlX3JhbmdlX2dpdmluZyA8LQ0KICBhZ2VfcmFuZ2VfZ2l2aW5nICU+JQ0KICBtdXRhdGUoYWdlX3JhbmdlID0gZmFjdG9yKGFnZV9yYW5nZSkpDQoNCmdncGxvdChhZ2VfcmFuZ2VfZ2l2aW5nLCBhZXMoYWdlX3JhbmdlLCBhdmdfZ2l2aW5nKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0PTEpKQ0KDQoNCmBgYA0KDQoNCjJiKSBDb3VudCBvZiBkb25vcnMgYmFzZWQgb24gYWdlIHJhbmdlIChhbm90aGVyIHdheSB0byBsb29rIGF0IGl0KQ0KDQoNCmBgYHtyfQ0KDQpnZ3Bsb3QoZGF0YWNsZWFuLCANCiAgICAgICBhZXMoYWdlX3JhbmdlKSkgKyANCiAgICAgICBnZW9tX2JhcigpICsgDQogICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3Q9MSkpICsgDQogIGxhYnModGl0bGUgPSAiQ291bnQgb2YgQWdlIFJhbmdlcyIsIHggPSAiIiwgeSA9ICIiKQ0KICANCg0KYGBgDQoNCjJjKSBCb3hwbG90IG9mIHRoZSBBZ2UgUmFuZ2VzIEFnYWluc3QgdGhlIExpZmV0aW1lIEdpdmluZyBBbW91bnRzIHdpdGggYSBsb2cgc2NhbGUgYXBwbGllZCAtIHRoZSByZWFzb24gd2UgYXBwbGllZCBsb2cgc2NhbGUgaXMgdG8gcmVzb2x2ZSBpc3N1ZXMgd2l0aCB2aXN1YWxpemF0aW9ucyB0aGF0IHNrZXcgdG93YXJkcyBsYXJnZSB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIA0KDQoNCmBgYHtyfQ0KDQpnZ3Bsb3QoZGF0YWNsZWFuLCBhZXMoYWdlX3JhbmdlLEhILkxpZmV0aW1lLkdpdmluZyxmaWxsID0gYWdlX3JhbmdlKSkgKyANCiAgZ2VvbV9ib3hwbG90KA0KICBvdXRsaWVyLmNvbG91ciA9ICJyZWQiKSArIA0KICBzY2FsZV95X2xvZzEwKCkgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSkpDQogIA0KDQpgYGANCg0KMmQpIFNwbGl0dGluZyBieSBhZ2UgYW5kIGdlbmRlciANCg0KDQpgYGB7cn0NCg0KDQojY3JlYXRpbmcgYm94cGxvdHMgDQpkYXRhY2xlYW4gJT4lIA0KICBmaWx0ZXIoQWdlIDwgMTAwKSAlPiUgI3JlbW92aW5nIHRoZSB3ZWlyZCBvdXRsaWVycyB0aGF0IGFyZSBvdmVyIDEwMCANCiAgZmlsdGVyKFNleCAlaW4lIGMoIk0iLCAiRiIpKSAlPiUNCiAgZ2dwbG90KGFlcyhTZXgsIEFnZSkpICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIHRoZW1lX2Vjb25vbWlzdCgpICsgDQogIGdndGl0bGUoIkFnZXMgb2YgRG9ub3JzIEJhc2VkIG9uIEdlbmRlciIpICsgDQogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpDQogIA0KDQpgYGANCkdpdmluZyBieSBnZW5kZXINCg0KDQpgYGB7cn0NCg0KI3JlbW92ZSBOQXMgVSBYIA0KcSA8LSBnZ3Bsb3QoZGF0YWNsZWFuKQ0KcSArIHN0YXRfc3VtbWFyeV9iaW4oDQogIGFlcyh5ID0gSEguTGlmZXRpbWUuR2l2aW5nLCB4ID0gc2V4X3NpbXBsZSksIA0KICBmdW4ueSA9ICJtZWFuIiwgZ2VvbSA9ICJiYXIiKQ0KDQpzdW1tYXJ5KGRhdGFjbGVhbiRzZXhfc2ltcGxlKQ0KDQpgYGANCg0KTWVhbiBhZ2UgYnkgZ2VuZGVyDQoNCg0KYGBge3J9DQojYnJlYWtkb3duIG9mIHNleHMgDQp0YWxseShncm91cF9ieShkYXRhY2xlYW4sIFNleCkpDQoNCnN1bW1hcml6ZShncm91cF9ieShkYXRhY2xlYW4sIFNleCksIA0KICAgICAgICAgIGF2Z19naXZpbmcgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICBhdmdfYWdlID0gbWVhbihBZ2UsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgbWVkX2FnZSA9IG1lZGlhbihBZ2UsIG5hLnJtID0gVFJVRSkpDQoNCiNncm91cGluZyBieSBzZXggYW5kIGFnZSByYW5nZSBmb3Igc2xpZGVzIA0KdGFsbHkoZ3JvdXBfYnkoZGF0YWNsZWFuLCBTZXgsIGFnZV9yYW5nZSkpDQoNCg0KDQpgYGANCg0KDQoNCjJlKSBEaXN0cmlidXRpb24gb2YgcGVvcGxlIGluIHRoZSBzdGF0ZXMgdGhhdCB0aGV5IGxpdmUuDQoNCmBgYHtyfQ0KDQogIGRhdGFjbGVhbiAlPiUNCiAgbXV0YXRlKFN0YXRlID0gaWZlbHNlKFN0YXRlID09ICIgIiwgIk5BIiwgU3RhdGUpKSAlPiUNCiAgZmlsdGVyKFN0YXRlICE9ICJOQSIpICU+JQ0KICBncm91cF9ieShTdGF0ZSkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IGxlbmd0aChTdGF0ZSkpICU+JQ0KICBmaWx0ZXIoQ291bnQgPiA4MDApICU+JQ0KICBhcnJhbmdlKC1Db3VudCkgJT4lDQogIGthYmxlKGNvbC5uYW1lcyA9IGMoIkRvbm9yJ3MgU3RhdGUiLCAiQ291bnQiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJjb25kZW5zZWQiKSwNCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikNCiAgDQogDQogIA0KICANCg0KDQpgYGANCg0KMmYpIExvb2tpbmcgYXQgYWxsIGRvbm9ycyBmaXJzdCBnaWZ0IGFtb3VudC4gNzUlIG1hZGUgYSBmaXJzdCBnaWZ0IG9mIDwxMDAuIA0KDQpgYGB7cn0NCg0KIG5vX25vbl9kb25vcnMgPC0gZGF0YWNsZWFuICU+JQ0KICBmaWx0ZXIoTGlmZXRpbWUuR2l2aW5nICE9IDApDQogIA0KbmQgPC0gcXVhbnRpbGUobm9fbm9uX2Rvbm9ycyRISC5GaXJzdC5HaWZ0LkFtb3VudCwgcHJvYnMgPSBjKC4yNSwuNTAsLjc1LC45LC45OSksIG5hLnJtID0gVFJVRSkNCg0KbmQgPC0gYXMuZGF0YS5mcmFtZShuZCkNCg0KbmQgJT4lDQogIGthYmxlKGNvbC5uYW1lcyA9ICJRdWFudGlsZSIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLA0KICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBGKQ0KICANCiAgDQoNCg0KYGBgDQoNCg0KDQojIyBNb2RlbGluZyBmb3IgeW91IA0KDQpTcGxpdCBkYXRhDQoNCmBgYCB7cn0NCiNjb252ZXJ0aW5nIG1hcnJpZWQgWSBhbmQgTiB0byAxIGFuZCAwIA0KZGF0YWNsZWFuIDwtIGRhdGFjbGVhbiAlPiUNCiAgICAgIG11dGF0ZShNYXJyaWVkX3NpbXBsZSA9IGlmZWxzZShNYXJyaWVkID09ICJOIiwwLDEpKQ0KDQoNCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lDQogIG11dGF0ZShoaC5saWZldGltZS5naXZpbmdfZmN0ID0gYXMuZmFjdG9yKEhILkxpZmV0aW1lLkdpdmluZykpDQoNCg0KbGlicmFyeSgicnNhbXBsZSIpDQoNCmRhdGFfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChkYXRhY2xlYW4sIHByb3AgPSAwLjc1KQ0KDQpkYXRhX3RyYWluIDwtIHRyYWluaW5nKGRhdGFfc3BsaXQpDQpkYXRhX3Rlc3QgPC0gdGVzdGluZyhkYXRhX3NwbGl0KQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KcCA8LSBkYXRhY2xlYW4gJT4lDQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLCBmaWxsID0gImJsdWUiKSArIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsNCiAgZ2d0aXRsZSgiT3ZlcmFsbCBEb25vciBBZ2UgRGlzdHJpYnV0aW9uIikgKyANCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDUsMTAwLGJ5ID0gMjApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMjAsMTAwLGJ5ID0gMjApKSArIHhsaW0oYygyMCwxMDApKQ0KDQpnZ3Bsb3RseShwKQ0KICANCnANCg0KZ2dwbG90KGRhdGEgPSBkYXRhY2xlYW4sIGFlcyh4ID0gQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0iYmx1ZSIpKyB4bGltKGMoMjAsMTAwKSkNCg0KICANCg0KDQpgYGANCg0KQW5vdGhlciBIaXN0b2dyYW0NCg0KDQpgYGB7cn0NCg0KZGF0YWNsZWFuICU+JQ0KICBmaWx0ZXIoQWdlID49IDEwKSAlPiUNCiAgZmlsdGVyKEFnZSA8PSA5MCkgJT4lDQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiMwMDI4NDUiLCBiaW5zID0gMjApICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKw0KICBnZ3RpdGxlKCJPdmVyYWxsIERvbm9yIEFnZSBEaXN0cmlidXRpb24iKSArIA0KICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsNSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEwMDAwMDAwLDIwMDApKSANCg0KYGBgDQpBZ2UgZGlzdHJpYnV0aW9uIGJ5IGdlbmRlciANCg0KYGBge3J9DQojQWdlIEdlbmRlciBmaWx0ZXJlZCBvdXQgYmVsb3cgMTUgYW5kIGFib3ZlIDkwIC0gYWxzbyByZW1vdmVkIFUgWCB0aGUgd2VpcmQgdmFsdWVzIA0KZGF0YWNsZWFuICU+JQ0KICBmaWx0ZXIoQWdlID49IDE1KSAlPiUNCiAgZmlsdGVyKEFnZSA8PSA5MCkgJT4lDQogIG11dGF0ZShTZXggPSBhcy5mYWN0b3IoU2V4KSkgJT4lDQogIGZpbHRlcihTZXggIT0gIlUiKSAlPiUNCiAgZmlsdGVyKFNleCAhPSAiWCIpICU+JQ0KICBnZ3Bsb3QoYWVzKEFnZSwgZmlsbCA9IFNleCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDI1KSArIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsNCiAgZ2d0aXRsZSgiQWdlIERpc3RyaWJ1dGlvbiBieSBHZW5kZXIiKSArIA0KICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsMTApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCw1MDAwMCwyMDAwKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI2ZmOTk3MyIsICIjMDBjZmNjIikpDQpgYGANCg0KRG9ub3IgYWdlIGRpc3RyaWJ1dGlvbiBieSBtYXJpdGFsIHN0YXR1cyANCg0KYGBge3J9DQojQWdlIE1hcml0YWwgU3RhdHVzDQpkYXRhY2xlYW4gJT4lDQogIGZpbHRlcihBZ2UgPj0gMjApICU+JQ0KICBmaWx0ZXIoQWdlIDw9IDg1KSAlPiUNCiAgZ2dwbG90KGFlcyhBZ2UsIGZpbGwgPSBNYXJyaWVkKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUpICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKw0KICBnZ3RpdGxlKCJPdmVyYWxsIERvbm9yIEFnZSBEaXN0cmlidXRpb24gYnkgTWFyaXRhbCBTdGF0dXMiKSArIA0KICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsNSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDUwMDAwLDIwMDApKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjZmY5OTczIiwgIiMwMGNmY2MiKSkNCmBgYA0KDQpMaW5lYXIgTW9kZWwgDQoNCmBgYHtyfQ0KI1RoZXNlIHdpbGwgZm9jdXMgb24gcHJlZGljdGluZyB3aGV0aGVyIGEgY29uc3RpdHVlbnQgaXMgYSBkb25vciBvciBub24tZG9ub3IuIA0KDQoNCm1vZDFsbSA8LSBsbSggTGlmZXRpbWUuR2l2aW5nIH4gTWFycmllZF9zaW1wbGUsDQogICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQ0KDQptb2QybG0gPC0gbG0oIFRvdGFsLkdpdmluZy5ZZWFycyB+IExpZmV0aW1lLkdpdmluZywNCiAgICAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4pDQoNCm1vZDNsbSA8LSBsbSggTGlmZXRpbWUuR2l2aW5nIH4gcmVnaW9uLA0KICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbikNCg0Kc3VtbWFyeShtb2QxbG0pDQpzdW1tYXJ5KG1vZDJsbSkNCnN1bW1hcnkobW9kM2xtKQ0KI2luY3JlYXNpbmcgdGhlIGdpdmluZyB5ZWFyIG9uZSB5ZWFyIGluY3JlYXNlIHRvdGFsIGdpdmluZyBieSAwLjAwMzUNCg0KDQpnZ3Bsb3QoZGF0YSA9IGRhdGFfdHJhaW4sIGFlcyh4ID0gQWdlLCB5ID0gbG9nKEhILkxpZmV0aW1lLkdpdmluZykpKSArIGdlb21fcG9pbnQoYWxwaGEgPSAxLzEwKSArIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSArIGZhY2V0X3dyYXAofnJlZ2lvbikgKyB0aGVtZV9jbGVhbihiYXNlX3NpemUgPSA4KSArIGxhYnMoeCA9ICJYIiwgeSA9ICJZIikgKw0KICAgICAgZ2d0aXRsZSgiUmVnaW9uIikNCg0KDQpnZ3Bsb3QoZGF0YSA9IGRhdGFfdHJhaW4sIGFlcyh4ID0gQWdlLCB5ID0gbG9nKEhILkxpZmV0aW1lLkdpdmluZykpKSArIGdlb21fcG9pbnQoYWxwaGEgPSAxLzEwKSArIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSArIGZhY2V0X3dyYXAofm5tYl9kZWdyZWUpICsgdGhlbWVfY2xlYW4oYmFzZV9zaXplID0gOCkgKyBsYWJzKHggPSAiWCIsIHkgPSAiWSIpICsNCiAgICAgIGdndGl0bGUoIk51bWJlciBvZiBEZWdyZWVzIikNCg0KDQpnZ3Bsb3QoZGF0YSA9IGRhdGFfdHJhaW4sIGFlcyh4ID0gQWdlLCB5ID0gbG9nKEhILkZpcnN0LkdpZnQuQW1vdW50KSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+ZG9ub3JzZWdfZmN0KSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArDQogICAgICBnZ3RpdGxlKCJEb25vciBTZWdtZW50IikNCg0KI1RoaXMgcGxvdCBhY3R1YWxseSBoYXMgc29tZSBpbnRlcmVzdGluZyByZXN1bHRzDQpnZ3Bsb3QoZGF0YSA9IGRhdGFfdHJhaW4sIGFlcyh4ID0gQWdlLCB5ID0gbG9nKExpZmV0aW1lLkdpdmluZykpKSArIGdlb21fcG9pbnQoYWxwaGEgPSAxLzEwKSArIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSArIGZhY2V0X3dyYXAofk5vX29mX0NoaWxkcmVuKSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArDQogICAgICBnZ3RpdGxlKCIjIENoaWxkcmVuIikNCg0KDQpkYXRhX3RyYWluICU+JSANCiAgc2VsZWN0X2lmKGlzLmZhY3RvcikgJT4lIA0KICBnbGltcHNlKCkNCg0KDQpgYGANCg0KDQoNCk1PUkUgTU9ERUxTDQoNCkJpZyBsb2dpc3RpYyBtb2RlbA0KDQpgYGB7cn0NCg0KIyBTZXQgZmFtaWx5IHRvIGJpbm9taWFsIHRvIHNldCBsb2dpc3RpYyBmdW5jdGlvbg0KIyBSdW4gdGhlIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBzZXQNCg0KZG9ub3JfbG9naXQxIDwtDQogIGdsbShoaC5saWZldGltZS5naXZpbmdfZmN0IH4gTWFycmllZF9zaW1wbGUsDQogICAgICBmYW1pbHkgPSAiYmlub21pYWwiLA0KICAgICAgZGF0YSA9IGRhdGFfdHJhaW4pDQoNCnN1bW1hcnkoZG9ub3JfbG9naXQxKQ0KDQoNCmRvbm9yX2xvZ2l0MiA8LQ0KICBnbG0oaGgubGlmZXRpbWUuZ2l2aW5nX2ZjdCB+IE5vX29mX0NoaWxkcmVuLA0KICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwNCiAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQ0KDQpzdW1tYXJ5KGRvbm9yX2xvZ2l0MikNCg0KDQoNCg0KDQoNCg0KI3N1bW1hcnkoZGF0YV90cmFpbiRtYWpvcl9naWZ0ZXIpDQoNCmRvbm9yX2xvZ2l0MyA8LQ0KICBnbG0obWFqb3JfZ2lmdGVyIH4gTWFycmllZF9zaW1wbGUgKyBOb19vZl9DaGlsZHJlbiArIGRvbm9yc2VnX3NpbXBsZSArIEFzc2lnbm1lbnRfZmxhZyArIFRvdGFsLkdpdmluZy5ZZWFycywNCiAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsDQogICAgICBkYXRhID0gZGF0YV90cmFpbikNCg0Kc3VtbWFyeShkb25vcl9sb2dpdDMpDQpleHAoZG9ub3JfbG9naXQzJGNvZWZmaWNpZW50cykNCg0KI3RyYWluaW5nIHByZWRpY3Rpb25zIGZvciBpbiBzYW1wbGUgcHJlZHMgDQpwcmVkc190cmFpbiA8LSBwcmVkaWN0KGRvbm9yX2xvZ2l0MywgbmV3ZGF0YSA9IGRhdGFfdHJhaW4sIHR5cGUgPSAicmVzcG9uc2UiKSANCg0KI3Rlc3QgcHJlZGljdHMgZm9yIE9PUyAob3V0IG9mIHNhbXBsZSkNCnByZWRzX3Rlc3QgPC0gcHJlZGljdChkb25vcl9sb2dpdDMsIG5ld2RhdGEgPSBkYXRhX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQpoZWFkKHByZWRzX3RyYWluKQ0KaGVhZChwcmVkc190ZXN0KQ0KDQoNCg0KcmVzdWx0c190cmFpbiA8LSBkYXRhLmZyYW1lKA0KICBgdHJ1dGhgID0gZGF0YV90cmFpbiAgICU+JSBzZWxlY3QobWFqb3JfZ2lmdGVyKSAlPiUgDQogICAgbXV0YXRlKG1ham9yX2dpZnRlciA9IGFzLm51bWVyaWMobWFqb3JfZ2lmdGVyKSksDQogIGBDbGFzczFgID0gIHByZWRzX3RyYWluLA0KICBgdHlwZWAgPSByZXAoInRyYWluIixsZW5ndGgocHJlZHNfdHJhaW4pKQ0KKQ0KDQpyZXN1bHRzX3Rlc3QgPC0gZGF0YS5mcmFtZSgNCiAgYHRydXRoYCA9IGRhdGFfdGVzdCAgICU+JSBzZWxlY3QobWFqb3JfZ2lmdGVyKSAlPiUgDQogICAgbXV0YXRlKG1ham9yX2dpZnRlciA9IGFzLm51bWVyaWMobWFqb3JfZ2lmdGVyKSksDQogIGBDbGFzczFgID0gIHByZWRzX3Rlc3QsDQogIGB0eXBlYCA9IHJlcCgidGVzdCIsbGVuZ3RoKHByZWRzX3Rlc3QpKQ0KKQ0KDQpyZXN1bHRzIDwtIGJpbmRfcm93cyhyZXN1bHRzX3RyYWluLHJlc3VsdHNfdGVzdCkNCg0KZGltKHJlc3VsdHNfdHJhaW4pDQpkaW0ocmVzdWx0c190ZXN0KQ0KZGltKHJlc3VsdHMpDQoNCmxpYnJhcnkoJ3Bsb3RST0MnKQ0KDQpwX3Bsb3QgPC0NCiAgZ2dwbG90KHJlc3VsdHMsDQogICAgICAgICBhZXMobSA9IENsYXNzMSwgZCA9IG1ham9yX2dpZnRlciwgY29sb3IgPSB0eXBlKSkgKw0KICBnZW9tX3JvYyhsYWJlbHNpemUgPSAyLjUsDQogICAgICAgICAgICNUb29rIHRoZSBsYWJlbHNpemUgZG93biB0byBhdm9pZCBjdXRvZmYNCiAgICAgICAgICAgY3V0b2Zmcy5hdCA9IGMoMC43LDAuNSwwLjMsMC4xLDApKSArDQogI1dlIHJlbW92ZWQgc29tZSBvZiB0aGUgY3V0b2ZmcyB0byBhdm9pZCB0aGUgbWFzaHVwIG5lYXIgdGhlIG9yaWdpbi4NCg0KICAjQ2hhbmdlZCB0aGUgdGhlbWUgdG8gYXZvaWQgY3V0b2ZmIHBsb3QgdmFsdWVzLg0KICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE0KSArIA0KICBsYWJzKHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsIA0KICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIikgKw0KICAgICAgZ2d0aXRsZSgiUk9DIFBsb3Q6IFRyYWluaW5nIGFuZCBUZXN0IikNCnByaW50KHBfcGxvdCkgDQoNCg0KcF90cmFpbiA8LQ0KICBnZ3Bsb3QocmVzdWx0c190cmFpbiwNCiAgICAgICAgIGFlcyhtID0gQ2xhc3MxLCBkID0gbWFqb3JfZ2lmdGVyLCBjb2xvciA9IHR5cGUpKSArDQogIGdlb21fcm9jKGxhYmVsc2l6ZSA9IDMuNSwNCiAgICAgICAgICAgY3V0b2Zmcy5hdCA9IGMoMC43LDAuNSwwLjMsMC4xLDApKSArDQogDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTYpICsgDQogIGxhYnMoeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwgDQogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiKSArDQogICAgICBnZ3RpdGxlKCJST0MgUGxvdDogVHJhaW5pbmcgYW5kIFRlc3QiKQ0KDQpwX3Rlc3QgPC0NCiAgZ2dwbG90KHJlc3VsdHNfdGVzdCwNCiAgICAgICAgIGFlcyhtID0gQ2xhc3MxLCBkID0gbWFqb3JfZ2lmdGVyLCBjb2xvciA9IHR5cGUpKSArDQogIGdlb21fcm9jKGxhYmVsc2l6ZSA9IDMuNSwNCiAgICAgICAgICAgY3V0b2Zmcy5hdCA9IGMoMC43LDAuNSwwLjMsMC4xLDApKSArDQogDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTYpICsgDQogIGxhYnMoeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwgDQogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiKSArDQogICAgICBnZ3RpdGxlKCJST0MgUGxvdDogVHJhaW5pbmcgYW5kIFRlc3QiKQ0KDQojQ2FsY3VsYXRpbmcgQVVDIG9mIGJvdGgNCnByaW50KGNhbGNfYXVjKHBfdHJhaW4pJEFVQykNCnByaW50KGNhbGNfYXVjKHBfdGVzdCkkQVVDKQ0KDQoNCg0KDQoNCg0KDQpgYGANCg0KUklER0UNCg0KYGBge3J9DQoNCmxpYnJhcnkoJ2dsbW5ldCcpDQpsaWJyYXJ5KCdnbG1uZXRVdGlscycpDQoNCnJpZGdlX2ZpdDEgPC0gY3YuZ2xtbmV0KEhILkxpZmV0aW1lLkdpdmluZyB+IHNleF9mY3QgKyBkb25vcnNlZ19mY3QgKyBOb19vZl9DaGlsZHJlbiwNCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4sDQogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMCkNCg0KI0FscGhhIDAgc2V0cyB0aGUgUmlkZ2UNCnByaW50KHJpZGdlX2ZpdDEpDQpwcmludChyaWRnZV9maXQxJGxhbWJkYS5taW4pDQoNCnByaW50KHJpZGdlX2ZpdDEkbGFtYmRhLjFzZSkNCg0KDQpgYGANCg0KTEFTU08NCg0KYGBge3J9DQoNCiNVc2luZyBjdi5nbG1uZXQgZnJvbSBjbGFzcw0KbHMoZGF0YV90cmFpbikgDQppcy5mYWN0b3IoZGF0YV90cmFpbiRtYWpvcl9naWZ0ZXIpDQpnbGltcHNlKGRhdGFfdHJhaW4kTGlmZXRpbWUuR2l2aW5nKQ0KDQpkYXRhX3RyYWluICU+JSANCiAgc2VsZWN0X2lmKGlzLmZhY3RvcikgJT4lIA0KICBnbGltcHNlKCkNCg0KDQpsaWJyYXJ5KGdsbW5ldCkNCmxpYnJhcnkoZ2xtbmV0VXRpbHMpDQpsYXNzb19maXQgPC0gY3YuZ2xtbmV0KExpZmV0aW1lLkdpdmluZyB+IGpvYnRpdGxlX3NpbXBsZSArIG5tYl9kZWdyZWUgKyBzY2hvb2wxX3NpbXBsZSArIGhoZmlyc3RnaWZ0X3NpbXBsZSArIG1hajFfc2ltcGxlICsgZG9ub3JzZWdfc2ltcGxlLA0KICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbiwNCiAgICAgICAgICAgICAgICAgICAgICAgI0FscGhhIDEgZm9yIGxhc3NvDQogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMSkNCg0KDQpwcmludChsYXNzb19maXQkbGFtYmRhLm1pbikNCiMNCnByaW50KGxhc3NvX2ZpdCRsYW1iZGEuMXNlKQ0KDQpwbG90KGxhc3NvX2ZpdCkNCg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KDQpjb2VmKGxhc3NvX2ZpdCkNCiNEZWZhdWx0IHNldHRpbmcgaXMgbGFtYmRhLjFzZQ0KDQojRnJvbSB0aGUgYm9vayAtIHNob3dpbmcgY29udmVyZ2VuY2Ugd2l0aCBsYW1iZGEgdmFsdWVzDQpwbG90KGxhc3NvX2ZpdCRnbG1uZXQuZml0LCB4dmFyPSJsYW1iZGEiKQ0KYWJsaW5lKHY9bG9nKGMobGFzc29fZml0JGxhbWJkYS5taW4sIGxhc3NvX2ZpdCRsYW1iZGEuMXNlKSksIGx0eT0yKQ0KDQpgYGANCg0KYGBge3J9DQoNCiNlbmV0X21vZCA8LSBjdmEuZ2xtbmV0KGRlcGVuZGVudCB+IGluZHkxICsgaW5keTIsDQojICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YSwNCiMgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gc2VxKDAsMSwgYnkgPSAwLjEpKQ0KDQojcHJpbnQoZW5ldF9tb2QpDQojcGxvdChlbmV0X21vZCkNCg0KDQpgYGANCg0KRUxBU1RJQ05FVA0KDQpgYGB7ciBlbGFzdGljbmV0fQ0KDQptaW5sb3NzcGxvdChlbmV0X21vZCwgDQogICAgICAgICAgICBjdi50eXBlID0gIm1pbiIpDQoNCmdldF9hbHBoYSA8LSBmdW5jdGlvbihmaXQpIHsNCiAgYWxwaGEgPC0gZml0JGFscGhhDQogIGVycm9yIDwtIHNhcHBseShmaXQkbW9kbGlzdCwgDQogICAgICAgICAgICAgICAgICBmdW5jdGlvbihtb2QpIHttaW4obW9kJGN2bSl9KQ0KICBhbHBoYVt3aGljaC5taW4oZXJyb3IpXQ0KfQ0KDQpnZXRfbW9kZWxfcGFyYW1zIDwtIGZ1bmN0aW9uKGZpdCkgew0KICBhbHBoYSA8LSBmaXQkYWxwaGENCiAgbGFtYmRhTWluIDwtIHNhcHBseShmaXQkbW9kbGlzdCwgYFtbYCwgImxhbWJkYS5taW4iKQ0KICBsYW1iZGFTRSA8LSBzYXBwbHkoZml0JG1vZGxpc3QsIGBbW2AsICJsYW1iZGEuMXNlIikNCiAgZXJyb3IgPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCBmdW5jdGlvbihtb2QpIHttaW4obW9kJGN2bSl9KQ0KICBiZXN0IDwtIHdoaWNoLm1pbihlcnJvcikNCiAgZGF0YS5mcmFtZShhbHBoYSA9IGFscGhhW2Jlc3RdLCBsYW1iZGFNaW4gPSBsYW1iZGFNaW5bYmVzdF0sDQogICAgICAgICAgICAgbGFtYmRhU0UgPSBsYW1iZGFTRVtiZXN0XSwgZXJvciA9IGVycm9yW2Jlc3RdKQ0KfQ0KDQpiZXN0X2FscGhhIDwtIGdldF9hbHBoYShlbmV0X21vZCkNCnByaW50KGJlc3RfYWxwaGEpDQpnZXRfbW9kZWxfcGFyYW1zKGVuZXRfbW9kKQ0KDQpiZXN0X21vZCA8LSBlbmV0X21vZCRtb2RsaXN0W1t3aGljaChlbmV0X21vZCRhbHBoYSA9PSBiZXN0X2FscGhhKV1dDQoNCnByaW50KGJlc3RfbW9kKQ0KDQoNCmBgYA0KDQpSaWRnZXMgcGxvdCAtIGNvdWxkIGJlIHVzZWZ1bCBmb3IgcGxvdHRpbmcgZG9uYXRpb25zIHZzIGRvbm9yIHNlZ21lbnQNCg0KYGBge3J9DQoNCmxpYnJhcnkoJ2dncmlkZ2VzJykNCg0Kc3VtbWFyeShkYXRhX3RyYWluJHZhcmlhYmxlKQ0KDQpnZ3Bsb3QoZGF0YV90cmFpbiwgYWVzKHggPSBISC5MaWZldGltZS5HaXZpbmcsIHkgPSBkb25vcnNlZ19mY3QpKSArIGdlb21fZGVuc2l0eV9yaWRnZXMocmVsX21pbl9oZWlnaHQgPSAwLjAwNSkgKyB4bGltKGMoMCwgNDAwKSkgKyANCiAgICAgIGdndGl0bGUoIkhIIExpZmV0aW1lIEdpdmluZyBieSBEb25vciBTZWdtZW50IikNCg0KYGBgDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KCdjb3JycGxvdCcpDQoNCiNyZW1vdmluZyBJRCB6aXAgYW5kIG5vbm51bWVyaWMgDQpjb3JycGxvdF9kYXRhIDwtIGRhdGFjbGVhblstYygxOjQ4LDUyOjU2LDU4OjYwLDYzLDY2OjY3LDcwOjcyLDc0OjgxLDgzOjEzMildDQoNCiNDb252ZXJ0IGZyb20gY2hhcmFjdGVyIHRvIG51bWVyaWMgZGF0YSB0eXBlDQpjb252ZXJ0X2ZhYzJudW0gPC0gZnVuY3Rpb24oeCl7DQogIGFzLm51bWVyaWMoYXMuZmFjdG9yKHgpKQ0KfQ0KDQpjb3JycGxvdF9kYXRhIDwtIG11dGF0ZV9hdChjb3JycGxvdF9kYXRhLA0KICAgICAgICAgICAgICAgICAgICAgLnZhcnMgPSBjKDE6MTIpLA0KICAgICAgICAgICAgICAgICAgICAgLmZ1bnMgPSBjb252ZXJ0X2ZhYzJudW0pDQojbWFraW5nIGEgbWF0cml4DQpjZF9jb3IgPC0gY29yKGNvcnJwbG90X2RhdGEpDQoNCiNjcmVhdGluZyBjb3JyZWxhdGlvbg0KY29sIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiI0JCNDQwMCIsICIjRUU5OTkwIiwgDQogICIjRkZGRkZGIiwgIiM3N0FBRUUiLCAiIzQ0NzdCQiIpKQ0KY29ycnBsb3QoY2RfY29yLCBtZXRob2Q9ImNvbG9yIiwgY29sPWNvbCgxMDApLA0KICB0eXBlPSJsb3dlciIsIGFkZENvZWYuY29sID0gImJsYWNrIiwNCiAgdGwucG9zPSJsdCIsIHRsLmNvbD0iYmxhY2siLCANCiAgdGwuY2V4PTAuNywgdGwuc3J0PTQ1LCANCiAgbnVtYmVyLmNleD0wLjcsDQogIGRpYWc9RkFMU0UpDQoNCiNjb3JyZWxhdGlvbiBtYXRyaXgNCiMgcGFpcnMofkFnZSArIE1vbnRocy5TaW5jZS5MYXN0LkdpZnQgKyBkb25vcnNlZ19mY3QgKyANCiMgICAgIG5tYl9kZWdyZWUgKyBOb19vZl9DaGlsZHJlbiArIEhILkZpcnN0LkdpZnQuQWdlICsgSEguRmlyc3QuR2lmdC5BbW91bnQgKyBUb3RhbC5HaXZpbmcuWWVhcnMsDQojICAgICBjb2wgPSBjb3JycGxvdF9kYXRhJEhILkxpZmV0aW1lLkdpdmluZywNCiMgICAgIGRhdGEgPSBjb3JycGxvdF9kYXRhLCANCiMgICAgIG1haW4gPSAiRG9ub3IgU2NhdHRlciBQbG90IE1hdHJpeCIpDQoNCiN3b3J0aGxlc3MuLiANCg0KZ2dwbG90KGRhdGEgPSBjb3JycGxvdF9kYXRhLCBhZXMoeCA9IG5tYl9kZWdyZWUsIHkgPSBISC5MaWZldGltZS5HaXZpbmcpKSArIA0KICBnZW9tX3BvaW50KGFwbGhhID0gMS8xMCkrDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0icmVkIikgDQoNCmBgYA0KDQoNClJhbmRvbSBGb3Jlc3QNCg0KYGBge3J9DQoNCmxpYnJhcnkoJ3JhbmRvbUZvcmVzdCcpDQoNCnJmX2ZpdF9kb25vciA8LSByYW5kb21Gb3Jlc3QoTGlmZXRpbWUuR2l2aW5nIH4gLiwgDQogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLA0KICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gY2xhc3NpZmljYXRpb24sDQogICAgICAgICAgICAgICAgICAgICAgIG10cnkgPSA3LA0KICAgICAgICAgICAgICAgICAgICAgICBuYS5hY3Rpb24gPSBuYS5yb3VnaGZpeCwNCiAgICAgICAgICAgICAgICAgICAgICAgbnRyZWUgPSAyMDAsDQogICAgICAgICAgICAgICAgICAgICAgIGltcG9ydGFuY2U9VFJVRQ0KICAgICAgICAgICAgICAgICAgICAgICApDQoNCnByaW50KHJmX2ZpdF9kb25vcikNCg0KDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA2fQ0KDQp2YXJJbXBQbG90KHJmX2ZpdF9kb25vciwgc29ydCA9IFRSVUUsIA0KICAgICAgICAgICBuLnZhciA9IDUsDQogICAgICAgICAgIHR5cGUgPSAyLCBjbGFzcyA9IE5VTEwsIHNjYWxlID0gVFJVRSwgDQogICAgICAgICAgIG1haW4gPSBkZXBhcnNlKHN1YnN0aXR1dGUocmZfZml0X2Rvbm9yKSkpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KCdyYW5kb21Gb3Jlc3RFeHBsYWluZXInKQ0KDQpwbG90X21pbl9kZXB0aF9kaXN0cmlidXRpb24oDQogIHJmX2ZpdF9kb25vciwNCiAgayA9IDEwLA0KICBtaW5fbm9fb2ZfdHJlZXMgPSAwLA0KICBtZWFuX3NhbXBsZSA9ICJ0b3BfdHJlZXMiLA0KICBtZWFuX3NjYWxlID0gRkFMU0UsDQogIG1lYW5fcm91bmQgPSAyLA0KICBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBtaW5pbWFsIGRlcHRoIGFuZCBpdHMgbWVhbiINCikNCg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KI1NwbGl0dGluZyBDYXRlZ29yeSBvdXQgdG8gY2hlY2sgaWYgdGhlIGNhdGVnb3J5IGlzIHVzZWZ1bCBmb3IgYW5hbHlzaXMNCmRhdGFfY2F0ZWdvcnlfc3BsaXRfb3V0IDwtIGRhdGFjbGVhbiAlPiUNCiAgbXV0YXRlKENhdGVnb3J5LkNvZGVzID0gdHJpbShzdHJzcGxpdChhcy5jaGFyYWN0ZXIoQ2F0ZWdvcnkuQ29kZXMpLCAifCIsIGZpeGVkID0gVFJVRSkpKSAlPiUNCiAgdW5uZXN0KENhdGVnb3J5LkNvZGVzKSAlPiUgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IENhdGVnb3J5LkNvZGVzLHZhbHVlc19mcm9tID1DYXRlZ29yeS5Db2RlcywgdmFsdWVzX2ZuID0gbGVuZ3RoKQ0KDQoNCmBgYA0K